<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>第11章 线程同步 | ShuangChenYue</title>
    <meta name="generator" content="VuePress 1.9.10">
    <link rel="icon" href="https://cdn.jsdelivr.net/gh/cmty256/imgs-blog@main/logo/白云.38zbldnhh180.jpg">
    <meta name="description" content="满招损，谦受益">
    <meta name="keywords" content="专注于Cpp语言的旅行者">
    
    <link rel="preload" href="/assets/css/0.styles.952d6952.css" as="style"><link rel="preload" href="/assets/js/app.67adcfd9.js" as="script"><link rel="preload" href="/assets/js/4.9aaa1650.js" as="script"><link rel="preload" href="/assets/js/1.5474518c.js" as="script"><link rel="preload" href="/assets/js/3.593d14fc.js" as="script"><link rel="preload" href="/assets/js/60.5cd0051a.js" as="script"><link rel="prefetch" href="/assets/js/10.3242746b.js"><link rel="prefetch" href="/assets/js/100.9224de43.js"><link rel="prefetch" href="/assets/js/101.f0d1b059.js"><link rel="prefetch" href="/assets/js/102.996bfc6d.js"><link rel="prefetch" href="/assets/js/103.9bfdbd6f.js"><link rel="prefetch" href="/assets/js/104.8613f283.js"><link rel="prefetch" href="/assets/js/105.aa6e809e.js"><link rel="prefetch" href="/assets/js/106.90192392.js"><link rel="prefetch" href="/assets/js/107.e82a40b7.js"><link rel="prefetch" href="/assets/js/108.994cd438.js"><link rel="prefetch" href="/assets/js/109.ec15acc2.js"><link rel="prefetch" href="/assets/js/11.c04b41c1.js"><link rel="prefetch" href="/assets/js/110.c32d8576.js"><link rel="prefetch" href="/assets/js/111.453b5d50.js"><link rel="prefetch" href="/assets/js/112.ffbdb3a4.js"><link rel="prefetch" href="/assets/js/113.12b8ad7d.js"><link rel="prefetch" href="/assets/js/114.899d2998.js"><link rel="prefetch" href="/assets/js/115.b7ad9576.js"><link rel="prefetch" href="/assets/js/116.a8394748.js"><link rel="prefetch" href="/assets/js/117.0edfe25b.js"><link rel="prefetch" href="/assets/js/118.9161b1fe.js"><link rel="prefetch" href="/assets/js/119.be59e21b.js"><link rel="prefetch" href="/assets/js/12.41437bf6.js"><link rel="prefetch" href="/assets/js/120.bcf439fb.js"><link rel="prefetch" href="/assets/js/121.c3d251b8.js"><link rel="prefetch" href="/assets/js/122.62b1caba.js"><link rel="prefetch" href="/assets/js/123.787c2ab0.js"><link rel="prefetch" href="/assets/js/124.a880746f.js"><link rel="prefetch" href="/assets/js/125.d8edfe7b.js"><link rel="prefetch" href="/assets/js/126.4ff01546.js"><link rel="prefetch" href="/assets/js/127.9416d1ff.js"><link rel="prefetch" href="/assets/js/128.01a4a7a0.js"><link rel="prefetch" href="/assets/js/129.76876665.js"><link rel="prefetch" href="/assets/js/13.922328e9.js"><link rel="prefetch" href="/assets/js/130.7f631dd9.js"><link rel="prefetch" href="/assets/js/131.c9e0fde9.js"><link rel="prefetch" href="/assets/js/132.1c04cde5.js"><link rel="prefetch" href="/assets/js/133.e8f381cd.js"><link rel="prefetch" href="/assets/js/134.03d19f8b.js"><link rel="prefetch" href="/assets/js/135.44607494.js"><link rel="prefetch" href="/assets/js/136.6a1eb3c9.js"><link rel="prefetch" href="/assets/js/137.27898fd1.js"><link rel="prefetch" href="/assets/js/138.5bc0cf54.js"><link rel="prefetch" href="/assets/js/139.c2d1addc.js"><link rel="prefetch" href="/assets/js/14.e54d7526.js"><link rel="prefetch" href="/assets/js/140.052ec8e4.js"><link rel="prefetch" href="/assets/js/141.131abb5a.js"><link rel="prefetch" href="/assets/js/142.6ba6c07b.js"><link rel="prefetch" href="/assets/js/143.5dd51d22.js"><link rel="prefetch" href="/assets/js/144.b45afca8.js"><link rel="prefetch" href="/assets/js/145.faa9fb04.js"><link rel="prefetch" href="/assets/js/146.b54c024d.js"><link rel="prefetch" href="/assets/js/147.a1223242.js"><link rel="prefetch" href="/assets/js/148.4767bcb2.js"><link rel="prefetch" href="/assets/js/149.b65ab046.js"><link rel="prefetch" href="/assets/js/15.7082a3da.js"><link rel="prefetch" href="/assets/js/150.9bd8c175.js"><link rel="prefetch" href="/assets/js/151.9f830e96.js"><link rel="prefetch" href="/assets/js/152.41cde7f0.js"><link rel="prefetch" href="/assets/js/153.f57d65e0.js"><link rel="prefetch" href="/assets/js/154.5d7c8d51.js"><link rel="prefetch" href="/assets/js/155.0ae99532.js"><link rel="prefetch" href="/assets/js/156.5a54e043.js"><link rel="prefetch" href="/assets/js/157.c25b5d40.js"><link rel="prefetch" href="/assets/js/158.aa025b46.js"><link rel="prefetch" href="/assets/js/159.47939d88.js"><link rel="prefetch" href="/assets/js/16.fc775b7b.js"><link rel="prefetch" href="/assets/js/160.f8624459.js"><link rel="prefetch" href="/assets/js/161.7a075dc2.js"><link rel="prefetch" href="/assets/js/162.1d48f266.js"><link rel="prefetch" href="/assets/js/163.5d68a99f.js"><link rel="prefetch" href="/assets/js/164.1262d0e5.js"><link rel="prefetch" href="/assets/js/165.2ccf0bdd.js"><link rel="prefetch" href="/assets/js/166.21ece4d9.js"><link rel="prefetch" href="/assets/js/167.bf8adb95.js"><link rel="prefetch" href="/assets/js/168.1cb8440d.js"><link rel="prefetch" href="/assets/js/169.1dd1e396.js"><link rel="prefetch" href="/assets/js/17.ecc7be70.js"><link rel="prefetch" href="/assets/js/170.c29ec18f.js"><link rel="prefetch" href="/assets/js/171.38820827.js"><link rel="prefetch" href="/assets/js/172.bbc8ffc6.js"><link rel="prefetch" href="/assets/js/173.470e21e7.js"><link rel="prefetch" href="/assets/js/174.3c2df318.js"><link rel="prefetch" href="/assets/js/175.d2690cdb.js"><link rel="prefetch" href="/assets/js/176.9ca64696.js"><link rel="prefetch" href="/assets/js/177.76f3271d.js"><link rel="prefetch" href="/assets/js/178.d7d9def2.js"><link rel="prefetch" href="/assets/js/179.b5644743.js"><link rel="prefetch" href="/assets/js/18.31fe7ecd.js"><link rel="prefetch" href="/assets/js/180.7592d5ef.js"><link rel="prefetch" href="/assets/js/181.5cb77d35.js"><link rel="prefetch" href="/assets/js/182.6fa5633c.js"><link rel="prefetch" href="/assets/js/183.b3a53d1b.js"><link rel="prefetch" href="/assets/js/184.3815c537.js"><link rel="prefetch" href="/assets/js/185.bcf4ab71.js"><link rel="prefetch" href="/assets/js/186.1cc02f6d.js"><link rel="prefetch" href="/assets/js/187.8b425fb7.js"><link rel="prefetch" href="/assets/js/188.44ccbd02.js"><link rel="prefetch" href="/assets/js/189.353b35e3.js"><link rel="prefetch" href="/assets/js/19.520992d5.js"><link rel="prefetch" href="/assets/js/190.c284595f.js"><link rel="prefetch" href="/assets/js/191.788ecc2d.js"><link rel="prefetch" href="/assets/js/192.712a164e.js"><link rel="prefetch" href="/assets/js/193.da58aba3.js"><link rel="prefetch" href="/assets/js/194.6b1b1f4d.js"><link rel="prefetch" href="/assets/js/195.c31d5c39.js"><link rel="prefetch" href="/assets/js/196.f6670c4d.js"><link rel="prefetch" href="/assets/js/197.5a1f50ab.js"><link rel="prefetch" href="/assets/js/2.ab565158.js"><link rel="prefetch" href="/assets/js/20.69e29cdc.js"><link rel="prefetch" href="/assets/js/21.2fd424ad.js"><link rel="prefetch" href="/assets/js/22.d4c0be54.js"><link rel="prefetch" href="/assets/js/23.4bb90ecc.js"><link rel="prefetch" href="/assets/js/24.c01be6b2.js"><link rel="prefetch" href="/assets/js/25.c8833687.js"><link rel="prefetch" href="/assets/js/26.8042b555.js"><link rel="prefetch" href="/assets/js/27.0d5fa4c0.js"><link rel="prefetch" href="/assets/js/28.f9735b8b.js"><link rel="prefetch" href="/assets/js/29.3af53626.js"><link rel="prefetch" href="/assets/js/30.5f1b56d1.js"><link rel="prefetch" href="/assets/js/31.544b2649.js"><link rel="prefetch" href="/assets/js/32.aa321988.js"><link rel="prefetch" href="/assets/js/33.6aba2c86.js"><link rel="prefetch" href="/assets/js/34.e1bbff24.js"><link rel="prefetch" href="/assets/js/35.233f76e0.js"><link rel="prefetch" href="/assets/js/36.cb773972.js"><link rel="prefetch" href="/assets/js/37.393d9c59.js"><link rel="prefetch" href="/assets/js/38.e2d530c5.js"><link rel="prefetch" href="/assets/js/39.acaf1cc0.js"><link rel="prefetch" href="/assets/js/40.358f731e.js"><link rel="prefetch" href="/assets/js/41.ded24b7e.js"><link rel="prefetch" href="/assets/js/42.b9f683c3.js"><link rel="prefetch" href="/assets/js/43.c8fb3e66.js"><link rel="prefetch" href="/assets/js/44.633142da.js"><link rel="prefetch" href="/assets/js/45.6095e772.js"><link rel="prefetch" href="/assets/js/46.421d8c7a.js"><link rel="prefetch" href="/assets/js/47.da50fe47.js"><link rel="prefetch" href="/assets/js/48.15ff5726.js"><link rel="prefetch" href="/assets/js/49.b662e624.js"><link rel="prefetch" href="/assets/js/5.c1b8a209.js"><link rel="prefetch" href="/assets/js/50.a8bc75df.js"><link rel="prefetch" href="/assets/js/51.51e36ae7.js"><link rel="prefetch" href="/assets/js/52.54cc6e51.js"><link rel="prefetch" href="/assets/js/53.4173561d.js"><link rel="prefetch" href="/assets/js/54.7cab8416.js"><link rel="prefetch" href="/assets/js/55.3d7317d3.js"><link rel="prefetch" href="/assets/js/56.3c22255b.js"><link rel="prefetch" href="/assets/js/57.18e46e30.js"><link rel="prefetch" href="/assets/js/58.aad57f31.js"><link rel="prefetch" href="/assets/js/59.7897f6a7.js"><link rel="prefetch" href="/assets/js/6.3131f88a.js"><link rel="prefetch" href="/assets/js/61.d9606403.js"><link rel="prefetch" href="/assets/js/62.aede9df0.js"><link rel="prefetch" href="/assets/js/63.2c30e554.js"><link rel="prefetch" href="/assets/js/64.18228ab7.js"><link rel="prefetch" href="/assets/js/65.27cb3fba.js"><link rel="prefetch" href="/assets/js/66.2fa6c2dc.js"><link rel="prefetch" href="/assets/js/67.d274a8df.js"><link rel="prefetch" href="/assets/js/68.3069cfcf.js"><link rel="prefetch" href="/assets/js/69.4c28600f.js"><link rel="prefetch" href="/assets/js/7.89e6165d.js"><link rel="prefetch" href="/assets/js/70.4175440c.js"><link rel="prefetch" href="/assets/js/71.2ee6b435.js"><link rel="prefetch" href="/assets/js/72.c75e3bb8.js"><link rel="prefetch" href="/assets/js/73.6f8b8211.js"><link rel="prefetch" href="/assets/js/74.6c7720cf.js"><link rel="prefetch" href="/assets/js/75.cccfb229.js"><link rel="prefetch" href="/assets/js/76.f022e5da.js"><link rel="prefetch" href="/assets/js/77.dab46206.js"><link rel="prefetch" href="/assets/js/78.ca574b2a.js"><link rel="prefetch" href="/assets/js/79.3d75e618.js"><link rel="prefetch" href="/assets/js/80.091749b1.js"><link rel="prefetch" href="/assets/js/81.14db0e21.js"><link rel="prefetch" href="/assets/js/82.8a2b1809.js"><link rel="prefetch" href="/assets/js/83.84a4b599.js"><link rel="prefetch" href="/assets/js/84.11d7c222.js"><link rel="prefetch" href="/assets/js/85.273d4388.js"><link rel="prefetch" href="/assets/js/86.fb40e20c.js"><link rel="prefetch" href="/assets/js/87.3316639e.js"><link rel="prefetch" href="/assets/js/88.dfc52200.js"><link rel="prefetch" href="/assets/js/89.8d615f6e.js"><link rel="prefetch" href="/assets/js/90.1d9f08bb.js"><link rel="prefetch" href="/assets/js/91.566813e7.js"><link rel="prefetch" href="/assets/js/92.d13c6f41.js"><link rel="prefetch" href="/assets/js/93.845c42a0.js"><link rel="prefetch" href="/assets/js/94.20a37b77.js"><link rel="prefetch" href="/assets/js/95.1a498005.js"><link rel="prefetch" href="/assets/js/96.39fa7f4b.js"><link rel="prefetch" href="/assets/js/97.50f7170e.js"><link rel="prefetch" href="/assets/js/98.dd2e15d6.js"><link rel="prefetch" href="/assets/js/99.ef7ea06a.js"><link rel="prefetch" href="/assets/js/vendors~docsearch.ae6b1de9.js">
    <link rel="stylesheet" href="/assets/css/0.styles.952d6952.css">
  </head>
  <body class="theme-mode-light">
    <div id="app" data-server-rendered="true"><div class="theme-container sidebar-open have-rightmenu"><header class="navbar blur"><div title="目录" class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="/" class="home-link router-link-active"><img src="https://cdn.jsdelivr.net/gh/cmty256/imgs-blog@main/logo/白云.38zbldnhh180.jpg" alt="ShuangChenYue" class="logo"> <span class="site-name can-hide">ShuangChenYue</span></a> <div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----></div> <nav class="nav-links can-hide"><div class="nav-item"><a href="/" class="nav-link">首页</a></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="CPP语言" class="dropdown-title"><!----> <span class="title" style="display:;">CPP语言</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/pages/c5bdd8/" class="nav-link">Cpp之旅</a></li><li class="dropdown-item"><!----> <a href="/pages/279e62/" class="nav-link">Cpp专栏</a></li><li class="dropdown-item"><!----> <a href="/pages/801755/" class="nav-link">Effective_CPP</a></li><li class="dropdown-item"><!----> <a href="/pages/6b2468/" class="nav-link">muduo网络库</a></li><li class="dropdown-item"><!----> <a href="/pages/5f8c9f/" aria-current="page" class="nav-link router-link-exact-active router-link-active">Unix环境高级编程</a></li><li class="dropdown-item"><!----> <a href="/pages/3f1d21/" class="nav-link">Cpp提高编程</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="计算机基础" class="dropdown-title"><!----> <span class="title" style="display:;">计算机基础</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/pages/7b1cb2/" class="nav-link">计算机网络</a></li><li class="dropdown-item"><!----> <a href="/pages/6048a8/" class="nav-link">操作系统</a></li><li class="dropdown-item"><!----> <a href="/pages/3b34ba/" class="nav-link">数据结构</a></li><li class="dropdown-item"><!----> <a href="/pages/412fe7/" class="nav-link">Linux</a></li><li class="dropdown-item"><!----> <a href="/pages/2dcfa1/" class="nav-link">算法</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="数据库" class="dropdown-title"><!----> <span class="title" style="display:;">数据库</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/pages/efa3f2/" class="nav-link">基础篇</a></li><li class="dropdown-item"><!----> <a href="/pages/ccc445/" class="nav-link">MySql</a></li><li class="dropdown-item"><!----> <a href="/pages/54616e/" class="nav-link">Redis</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="嵌入式软件开发" class="dropdown-title"><!----> <span class="title" style="display:;">嵌入式软件开发</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/pages/d142c2/" class="nav-link">电子嵌入式通信协议</a></li><li class="dropdown-item"><!----> <a href="/pages/4c6bf3/" class="nav-link">深入浅出SSD</a></li><li class="dropdown-item"><!----> <a href="/pages/d3f36a/" class="nav-link">文件系统</a></li><li class="dropdown-item"><!----> <a href="/pages/e0cca7/" class="nav-link">汇编语言</a></li><li class="dropdown-item"><!----> <a href="/pages/fab2d7/" class="nav-link">STM32</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="开发日常" class="dropdown-title"><!----> <span class="title" style="display:;">开发日常</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/pages/e472d1/" class="nav-link">随笔（持续更新）</a></li><li class="dropdown-item"><!----> <a href="/pages/71f6ae/" class="nav-link">Git知识总结</a></li><li class="dropdown-item"><!----> <a href="/pages/db6fb8/" class="nav-link">Git备忘清单</a></li><li class="dropdown-item"><!----> <a href="/pages/e1081f/" class="nav-link">Git 创建删除远程分支</a></li><li class="dropdown-item"><!----> <a href="/pages/777b8a/" class="nav-link">nvm使用小结</a></li><li class="dropdown-item"><!----> <a href="/pages/ee770e/" class="nav-link">虚拟机固定 IP 地址</a></li><li class="dropdown-item"><!----> <a href="/pages/1ab9a6/" class="nav-link">Shell 脚本学习笔记</a></li><li class="dropdown-item"><!----> <a href="/pages/411aa4/" class="nav-link">VScode 插件 CodeGeeX 使用教程</a></li><li class="dropdown-item"><!----> <a href="/pages/0d525d/" class="nav-link">KylinV10 将项目上传至 Github教程</a></li><li class="dropdown-item"><!----> <a href="/pages/907786/" class="nav-link">KylinV10 安装 MySQL 教程（可防踩雷）</a></li><li class="dropdown-item"><!----> <a href="/pages/a2d21e/" class="nav-link">kylinV10-SP1 安装 QT</a></li><li class="dropdown-item"><!----> <a href="/pages/b561cf/" class="nav-link">高并发内存池</a></li><li class="dropdown-item"><!----> <a href="/pages/6ab6d1/" class="nav-link">USBGUARD 项目编译环境配置</a></li><li class="dropdown-item"><!----> <a href="/pages/883f02/" class="nav-link">Power_Destory 项目</a></li><li class="dropdown-item"><!----> <a href="/pages/479472/" class="nav-link">U 盘清除工具编译教程</a></li><li class="dropdown-item"><!----> <a href="/pages/9c4241/" class="nav-link">个人博客代码推送教程</a></li><li class="dropdown-item"><!----> <a href="/pages/3ad765/" class="nav-link">SVN Trunk Branches的Merge操作</a></li><li class="dropdown-item"><!----> <a href="/pages/0c0ca8/" class="nav-link">如何高效阅读嵌入式项目代码</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="前端学习" class="dropdown-title"><!----> <span class="title" style="display:;">前端学习</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/pages/99897f/" class="nav-link">HTML与CSS</a></li><li class="dropdown-item"><!----> <a href="/pages/51542d/" class="nav-link">JS学习</a></li><li class="dropdown-item"><!----> <a href="/pages/803f9d/" class="nav-link">Vue3入门</a></li><li class="dropdown-item"><!----> <a href="/pages/ca4cfb/" class="nav-link">Vue3进阶</a></li><li class="dropdown-item"><!----> <a href="/pages/50e8d3/" class="nav-link">黑马Vue3</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="MFC" class="dropdown-title"><!----> <span class="title" style="display:;">MFC</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/pages/a4b108/" class="nav-link">MFC编程随记</a></li><li class="dropdown-item"><!----> <a href="/pages/41acbd/" class="nav-link">MFC实现ini配置文件的读取</a></li><li class="dropdown-item"><!----> <a href="/pages/951a7a/" class="nav-link">MFC实现点击列表头排序</a></li><li class="dropdown-item"><!----> <a href="/pages/a8598f/" class="nav-link">贴图法美化Button按钮</a></li><li class="dropdown-item"><!----> <a href="/pages/054516/" class="nav-link">MFC使用细节</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="闪存" class="dropdown-title"><!----> <span class="title" style="display:;">闪存</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/pages/b925b8/" class="nav-link">如何高效阅读嵌入式项目代码</a></li><li class="dropdown-item"><!----> <a href="/pages/28ec23/" class="nav-link">NAND Flash</a></li><li class="dropdown-item"><!----> <a href="/pages/62bf40/" class="nav-link">ARM 处理器</a></li><li class="dropdown-item"><!----> <a href="/pages/1a9374/" class="nav-link">嵌入式基础知识-存储器</a></li><li class="dropdown-item"><!----> <a href="/pages/aac5e3/" class="nav-link">闪存存储和制造技术概述</a></li><li class="dropdown-item"><!----> <a href="/pages/8f6056/" class="nav-link">芯片IO驱动力</a></li><li class="dropdown-item"><!----> <a href="/pages/d146b8/" class="nav-link">主流先进封装技术介绍</a></li><li class="dropdown-item"><!----> <a href="/pages/16f0ba/" class="nav-link">NAND Flash基础</a></li><li class="dropdown-item"><!----> <a href="/pages/90d8d0/" class="nav-link">基于PA算法的FTL引导</a></li><li class="dropdown-item"><!----> <a href="/pages/eb672b/" class="nav-link">SD逻辑擦除和物理擦除</a></li><li class="dropdown-item"><!----> <a href="/pages/747121/" class="nav-link">NAND Flash的SDR、ONFI、DDR接口</a></li><li class="dropdown-item"><!----> <a href="/pages/1eb351/" class="nav-link">【详解】Nand Flash必看知识</a></li><li class="dropdown-item"><!----> <a href="/pages/d2512a/" class="nav-link">【两万字详解】Nand Flash必看知识</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="面经" class="dropdown-title"><!----> <span class="title" style="display:;">面经</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/pages/d69946/" class="nav-link">虎牙C++技术面经</a></li><li class="dropdown-item"><!----> <a href="/pages/29251d/" class="nav-link">金山一面复习</a></li><li class="dropdown-item"><!----> <a href="/pages/c7c01f/" class="nav-link">完美世界秋招 C++ 游戏开发面经(Cpp部分)</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="其它" class="dropdown-title"><!----> <span class="title" style="display:;">其它</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/pages/fa256e/" class="nav-link">博客搭建</a></li><li class="dropdown-item"><!----> <a href="/pages/335531/" class="nav-link">网站收藏箱</a></li></ul></div></div> <!----></nav></div></header> <div class="sidebar-mask"></div> <div class="sidebar-hover-trigger"></div> <aside class="sidebar" style="display:none;"><!----> <nav class="nav-links"><div class="nav-item"><a href="/" class="nav-link">首页</a></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="CPP语言" class="dropdown-title"><!----> <span class="title" style="display:;">CPP语言</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/pages/c5bdd8/" class="nav-link">Cpp之旅</a></li><li class="dropdown-item"><!----> <a href="/pages/279e62/" class="nav-link">Cpp专栏</a></li><li class="dropdown-item"><!----> <a href="/pages/801755/" class="nav-link">Effective_CPP</a></li><li class="dropdown-item"><!----> <a href="/pages/6b2468/" class="nav-link">muduo网络库</a></li><li class="dropdown-item"><!----> <a href="/pages/5f8c9f/" aria-current="page" class="nav-link router-link-exact-active router-link-active">Unix环境高级编程</a></li><li class="dropdown-item"><!----> <a href="/pages/3f1d21/" class="nav-link">Cpp提高编程</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="计算机基础" class="dropdown-title"><!----> <span class="title" style="display:;">计算机基础</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/pages/7b1cb2/" class="nav-link">计算机网络</a></li><li class="dropdown-item"><!----> <a href="/pages/6048a8/" class="nav-link">操作系统</a></li><li class="dropdown-item"><!----> <a href="/pages/3b34ba/" class="nav-link">数据结构</a></li><li class="dropdown-item"><!----> <a href="/pages/412fe7/" class="nav-link">Linux</a></li><li class="dropdown-item"><!----> <a href="/pages/2dcfa1/" class="nav-link">算法</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="数据库" class="dropdown-title"><!----> <span class="title" style="display:;">数据库</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/pages/efa3f2/" class="nav-link">基础篇</a></li><li class="dropdown-item"><!----> <a href="/pages/ccc445/" class="nav-link">MySql</a></li><li class="dropdown-item"><!----> <a href="/pages/54616e/" class="nav-link">Redis</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="嵌入式软件开发" class="dropdown-title"><!----> <span class="title" style="display:;">嵌入式软件开发</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/pages/d142c2/" class="nav-link">电子嵌入式通信协议</a></li><li class="dropdown-item"><!----> <a href="/pages/4c6bf3/" class="nav-link">深入浅出SSD</a></li><li class="dropdown-item"><!----> <a href="/pages/d3f36a/" class="nav-link">文件系统</a></li><li class="dropdown-item"><!----> <a href="/pages/e0cca7/" class="nav-link">汇编语言</a></li><li class="dropdown-item"><!----> <a href="/pages/fab2d7/" class="nav-link">STM32</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="开发日常" class="dropdown-title"><!----> <span class="title" style="display:;">开发日常</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/pages/e472d1/" class="nav-link">随笔（持续更新）</a></li><li class="dropdown-item"><!----> <a href="/pages/71f6ae/" class="nav-link">Git知识总结</a></li><li class="dropdown-item"><!----> <a href="/pages/db6fb8/" class="nav-link">Git备忘清单</a></li><li class="dropdown-item"><!----> <a href="/pages/e1081f/" class="nav-link">Git 创建删除远程分支</a></li><li class="dropdown-item"><!----> <a href="/pages/777b8a/" class="nav-link">nvm使用小结</a></li><li class="dropdown-item"><!----> <a href="/pages/ee770e/" class="nav-link">虚拟机固定 IP 地址</a></li><li class="dropdown-item"><!----> <a href="/pages/1ab9a6/" class="nav-link">Shell 脚本学习笔记</a></li><li class="dropdown-item"><!----> <a href="/pages/411aa4/" class="nav-link">VScode 插件 CodeGeeX 使用教程</a></li><li class="dropdown-item"><!----> <a href="/pages/0d525d/" class="nav-link">KylinV10 将项目上传至 Github教程</a></li><li class="dropdown-item"><!----> <a href="/pages/907786/" class="nav-link">KylinV10 安装 MySQL 教程（可防踩雷）</a></li><li class="dropdown-item"><!----> <a href="/pages/a2d21e/" class="nav-link">kylinV10-SP1 安装 QT</a></li><li class="dropdown-item"><!----> <a href="/pages/b561cf/" class="nav-link">高并发内存池</a></li><li class="dropdown-item"><!----> <a href="/pages/6ab6d1/" class="nav-link">USBGUARD 项目编译环境配置</a></li><li class="dropdown-item"><!----> <a href="/pages/883f02/" class="nav-link">Power_Destory 项目</a></li><li class="dropdown-item"><!----> <a href="/pages/479472/" class="nav-link">U 盘清除工具编译教程</a></li><li class="dropdown-item"><!----> <a href="/pages/9c4241/" class="nav-link">个人博客代码推送教程</a></li><li class="dropdown-item"><!----> <a href="/pages/3ad765/" class="nav-link">SVN Trunk Branches的Merge操作</a></li><li class="dropdown-item"><!----> <a href="/pages/0c0ca8/" class="nav-link">如何高效阅读嵌入式项目代码</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="前端学习" class="dropdown-title"><!----> <span class="title" style="display:;">前端学习</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/pages/99897f/" class="nav-link">HTML与CSS</a></li><li class="dropdown-item"><!----> <a href="/pages/51542d/" class="nav-link">JS学习</a></li><li class="dropdown-item"><!----> <a href="/pages/803f9d/" class="nav-link">Vue3入门</a></li><li class="dropdown-item"><!----> <a href="/pages/ca4cfb/" class="nav-link">Vue3进阶</a></li><li class="dropdown-item"><!----> <a href="/pages/50e8d3/" class="nav-link">黑马Vue3</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="MFC" class="dropdown-title"><!----> <span class="title" style="display:;">MFC</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/pages/a4b108/" class="nav-link">MFC编程随记</a></li><li class="dropdown-item"><!----> <a href="/pages/41acbd/" class="nav-link">MFC实现ini配置文件的读取</a></li><li class="dropdown-item"><!----> <a href="/pages/951a7a/" class="nav-link">MFC实现点击列表头排序</a></li><li class="dropdown-item"><!----> <a href="/pages/a8598f/" class="nav-link">贴图法美化Button按钮</a></li><li class="dropdown-item"><!----> <a href="/pages/054516/" class="nav-link">MFC使用细节</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="闪存" class="dropdown-title"><!----> <span class="title" style="display:;">闪存</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/pages/b925b8/" class="nav-link">如何高效阅读嵌入式项目代码</a></li><li class="dropdown-item"><!----> <a href="/pages/28ec23/" class="nav-link">NAND Flash</a></li><li class="dropdown-item"><!----> <a href="/pages/62bf40/" class="nav-link">ARM 处理器</a></li><li class="dropdown-item"><!----> <a href="/pages/1a9374/" class="nav-link">嵌入式基础知识-存储器</a></li><li class="dropdown-item"><!----> <a href="/pages/aac5e3/" class="nav-link">闪存存储和制造技术概述</a></li><li class="dropdown-item"><!----> <a href="/pages/8f6056/" class="nav-link">芯片IO驱动力</a></li><li class="dropdown-item"><!----> <a href="/pages/d146b8/" class="nav-link">主流先进封装技术介绍</a></li><li class="dropdown-item"><!----> <a href="/pages/16f0ba/" class="nav-link">NAND Flash基础</a></li><li class="dropdown-item"><!----> <a href="/pages/90d8d0/" class="nav-link">基于PA算法的FTL引导</a></li><li class="dropdown-item"><!----> <a href="/pages/eb672b/" class="nav-link">SD逻辑擦除和物理擦除</a></li><li class="dropdown-item"><!----> <a href="/pages/747121/" class="nav-link">NAND Flash的SDR、ONFI、DDR接口</a></li><li class="dropdown-item"><!----> <a href="/pages/1eb351/" class="nav-link">【详解】Nand Flash必看知识</a></li><li class="dropdown-item"><!----> <a href="/pages/d2512a/" class="nav-link">【两万字详解】Nand Flash必看知识</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="面经" class="dropdown-title"><!----> <span class="title" style="display:;">面经</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/pages/d69946/" class="nav-link">虎牙C++技术面经</a></li><li class="dropdown-item"><!----> <a href="/pages/29251d/" class="nav-link">金山一面复习</a></li><li class="dropdown-item"><!----> <a href="/pages/c7c01f/" class="nav-link">完美世界秋招 C++ 游戏开发面经(Cpp部分)</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="其它" class="dropdown-title"><!----> <span class="title" style="display:;">其它</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/pages/fa256e/" class="nav-link">博客搭建</a></li><li class="dropdown-item"><!----> <a href="/pages/335531/" class="nav-link">网站收藏箱</a></li></ul></div></div> <!----></nav>  <ul class="sidebar-links"><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading"><span>Cpp之旅</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading"><span>Cpp专栏</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading"><span>Effetcive_CPP</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading"><span>muduo网络库</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading open"><span>Unix环境高级编程</span> <span class="arrow down"></span></p> <ul class="sidebar-links sidebar-group-items"><li><a href="/pages/5f8c9f/" aria-current="page" class="active sidebar-link">第11章 线程同步</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header level2"><a href="/pages/5f8c9f/#_1-1-举个栗子" class="sidebar-link">1.1 举个栗子</a></li><li class="sidebar-sub-header level2"><a href="/pages/5f8c9f/#_1-2-再举个栗子" class="sidebar-link">1.2 再举个栗子</a></li><li class="sidebar-sub-header level2"><a href="/pages/5f8c9f/#_1-3-再再举个栗子" class="sidebar-link">1.3 再再举个栗子</a></li><li class="sidebar-sub-header level2"><a href="/pages/5f8c9f/#_1-4-其他情况" class="sidebar-link">1.4 其他情况</a></li><li class="sidebar-sub-header level2"><a href="/pages/5f8c9f/#_3-1-理解" class="sidebar-link">3.1 理解</a></li><li class="sidebar-sub-header level2"><a href="/pages/5f8c9f/#_3-2-函数" class="sidebar-link">3.2 函数</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header level3"><a href="/pages/5f8c9f/#初始化" class="sidebar-link">初始化</a></li><li class="sidebar-sub-header level3"><a href="/pages/5f8c9f/#加锁、解锁" class="sidebar-link">加锁、解锁</a></li></ul></li><li class="sidebar-sub-header level2"><a href="/pages/5f8c9f/#_3-3-来个栗子" class="sidebar-link">3.3 来个栗子</a></li><li class="sidebar-sub-header level2"><a href="/pages/5f8c9f/#_4-1-理解" class="sidebar-link">4.1 理解</a></li><li class="sidebar-sub-header level2"><a href="/pages/5f8c9f/#_4-2-函数" class="sidebar-link">4.2 函数</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header level3"><a href="/pages/5f8c9f/#初始化-2" class="sidebar-link">初始化</a></li><li class="sidebar-sub-header level3"><a href="/pages/5f8c9f/#注意事项" class="sidebar-link">注意事项</a></li></ul></li><li class="sidebar-sub-header level2"><a href="/pages/5f8c9f/#_4-3-举个例子" class="sidebar-link">4.3 举个例子</a></li><li class="sidebar-sub-header level2"><a href="/pages/5f8c9f/#_5-1-引言" class="sidebar-link">5.1 引言</a></li><li class="sidebar-sub-header level2"><a href="/pages/5f8c9f/#_5-2-函数" class="sidebar-link">5.2 函数</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header level3"><a href="/pages/5f8c9f/#初始化-3" class="sidebar-link">初始化</a></li><li class="sidebar-sub-header level3"><a href="/pages/5f8c9f/#加锁、解锁-2" class="sidebar-link">加锁、解锁</a></li></ul></li><li class="sidebar-sub-header level2"><a href="/pages/5f8c9f/#_5-3-举个栗子" class="sidebar-link">5.3 举个栗子</a></li><li class="sidebar-sub-header level2"><a href="/pages/5f8c9f/#_6-1-理解" class="sidebar-link">6.1 理解</a></li><li class="sidebar-sub-header level2"><a href="/pages/5f8c9f/#_6-2-函数" class="sidebar-link">6.2 函数</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header level3"><a href="/pages/5f8c9f/#初始化-4" class="sidebar-link">初始化</a></li><li class="sidebar-sub-header level3"><a href="/pages/5f8c9f/#加锁、解锁-3" class="sidebar-link">加锁、解锁</a></li></ul></li><li class="sidebar-sub-header level2"><a href="/pages/5f8c9f/#_7-1-引言" class="sidebar-link">7.1 引言</a></li><li class="sidebar-sub-header level2"><a href="/pages/5f8c9f/#_7-2-函数" class="sidebar-link">7.2 函数</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header level3"><a href="/pages/5f8c9f/#初始化-5" class="sidebar-link">初始化</a></li></ul></li><li class="sidebar-sub-header level2"><a href="/pages/5f8c9f/#_7-3-举个例子" class="sidebar-link">7.3 举个例子</a></li></ul></li></ul></section></li><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading"><span>Cpp提高编程</span> <span class="arrow right"></span></p> <!----></section></li></ul> </aside> <div><main class="page"><div class="theme-vdoing-wrapper "><div class="articleInfo-wrap" data-v-06225672><div class="articleInfo" data-v-06225672><ul class="breadcrumbs" data-v-06225672><li data-v-06225672><a href="/" title="首页" class="iconfont icon-home router-link-active" data-v-06225672></a></li> <li data-v-06225672><span data-v-06225672>CPP语言</span></li><li data-v-06225672><span data-v-06225672>Unix环境高级编程</span></li></ul> <div class="info" data-v-06225672><div title="作者" class="author iconfont icon-touxiang" data-v-06225672><a href="javascript:;" data-v-06225672>霜晨月</a></div> <div title="创建时间" class="date iconfont icon-riqi" data-v-06225672><a href="javascript:;" data-v-06225672>2023-12-01</a></div> <!----></div></div></div> <!----> <div class="content-wrapper"><div class="right-menu-wrapper"><div class="right-menu-margin"><div class="right-menu-title">目录</div> <div class="right-menu-content"></div></div></div> <h1><img src="">第11章 线程同步<!----></h1> <!----> <div class="theme-vdoing-content content__default"><h1 id="第11章-线程同步"><a href="#第11章-线程同步" class="header-anchor">#</a> 第11章 线程同步</h1> <h1 id="_1、引言"><a href="#_1、引言" class="header-anchor">#</a> 1、引言</h1> <ul><li>当多个控制线程共享相同的内存时，需要确保每个线程看到一致的数据视图。</li> <li>如果每个线程使用的变量都是其他线程不会读取和修改的，那么就不存在一致性问题。</li> <li>同样，如果变量是只读的，多个线程同时读取该变量也不会有一致性问题。</li></ul> <p>但是，当一个线程可以修改的变量，其他线程也可以读取或者修改的时候，我们就需要对这些线程进行同步，确保它们在访问变量的存储内容时不会访问到无效的值。</p> <h2 id="_1-1-举个栗子"><a href="#_1-1-举个栗子" class="header-anchor">#</a> 1.1 举个栗子</h2> <blockquote><p>在变量修改时间多于一个存储器访问周期的处理器结构中，当存储器读与存储器写这两个周期交叉时，这种不一致就会出现。</p></blockquote> <p><img alt="1701423131369" data-src="https://cdn.jsdelivr.net/gh/xiaose-code/Pictures@main/img/1701423131369.webp" loading="lazy" class="lazy"></p> <p>图 11-7 描述了两个线程读写相同变量的假设例子。</p> <ul><li>在这个例子中，线程 A读取变量然后给这个变量赋予一个新的数值，但写操作需要两个存储器周期。</li> <li>当线程B在这两个存储器写周期中间读取这个变量时，它就会得到不一致的值。</li></ul> <h2 id="_1-2-再举个栗子"><a href="#_1-2-再举个栗子" class="header-anchor">#</a> 1.2 再举个栗子</h2> <p>为了解决这个问题，线程不得不使用锁，同一时间只允许一个线程访问该变量。</p> <p><img alt="1698388930790" data-src="https://cdn.jsdelivr.net/gh/xiaose-code/Pictures@main/img/1698388930790.webp" loading="lazy" class="lazy"></p> <p>图11-8描述了这种同步。</p> <ul><li>如果线程B希望读取变量，它首先要获取锁。</li> <li>同样，当线程A更新变量时，也需要获取同样的这把锁。</li> <li>这样，线程B在线程A释放锁以前就不能读取变量。</li></ul> <h2 id="_1-3-再再举个栗子"><a href="#_1-3-再再举个栗子" class="header-anchor">#</a> 1.3 再再举个栗子</h2> <p>两个或多个线程试图在同一时间修改同一变量时，也需要进行同步。考虑
变量增量操作的情况（图11-9）</p> <p>增量操作通常分解为以下3步：</p> <ol><li>从内存单元读入寄存器。</li> <li>在寄存器中对变量做增量操作。</li> <li>把新的值写回内存单元。</li></ol> <p><img alt="1698389111949" data-src="https://cdn.jsdelivr.net/gh/xiaose-code/Pictures@main/img/1698389111949.webp" loading="lazy" class="lazy"></p> <p>如果两个线程试图几乎在同一时间对同一个变量做增量操作而不进行同步的话，结果就可能出现不一致。</p> <ul><li>变量可能比原来增加了1，也有可能比原来增加了2，具体增加了1还是2要取决于第二个线程开始操作时获取的数值。</li> <li>如果第二个线程执行第1步要比第一个线程执行第3步要早，第二个线程读到的值与第一个线程一样，为变量加1，然后写回去，事实上没有实际的效果，总的来说变量只增加了1。</li></ul> <p>如果修改操作是原子操作，那么就不存在竞争。</p> <p>在图 11-9 例子中，如果增加1只需要一个存储器周期，那么就没有竞争存在。</p> <ul><li>如果数据总是以顺序一致出现的，就不需要额外的同步。</li> <li>当多个线程观察不到数据的不一致时，那么操作就是顺序一致的。</li></ul> <blockquote><p>但是，在现代计算机系统中，存储访问需要多个总线周期，多处理器的总线周期通常在多个处理器上是交叉的，所以我们并不能保证数据是顺序一致的。</p></blockquote> <ul><li>在顺序一致环境中，可以把数据修改操作解释为运行线程的顺序操作步骤。</li> <li>可以把这样的操作描述为“线程A对变量增加了1，然后线程B对变量增加了1，所以变量的值就比原来的大2”，或者描述为“线程B对变量增加了1，然后线程A对变量增加了1，所以变量的值就比原来的大2”。</li> <li>这两个线程的任何操作顺序都不可能让变量出现除了上述值以外的其他值。</li></ul> <h2 id="_1-4-其他情况"><a href="#_1-4-其他情况" class="header-anchor">#</a> 1.4 其他情况</h2> <blockquote><p>程序使用变量的方式也会引起竞争，也会导致不一致的情况发生。</p></blockquote> <p>例如，我们可能对某个变量加 1，然后基于这个值做出某种决定。因为这个增量操作步骤和这个决定步骤的组合并非原子操作，所以就给不一致情况的出现提供了可能。</p> <h1 id="_2、线程同步方法"><a href="#_2、线程同步方法" class="header-anchor">#</a> 2、线程同步方法</h1> <h1 id="_3、互斥量"><a href="#_3、互斥量" class="header-anchor">#</a> 3、互斥量</h1> <h2 id="_3-1-理解"><a href="#_3-1-理解" class="header-anchor">#</a> 3.1 理解</h2> <ul><li>互斥量（mutex）从本质上说是一把锁，在访问共享资源前对互斥量进行设置（加锁），在访问完成后释放（解锁）互斥量。</li> <li>对互斥量进行加锁以后，任何其他试图再次对互斥量加锁的线程都会被阻塞直到当前线程释放该互斥锁。</li> <li>如果释放互斥量时有一个以上的线程阻塞，那么所有该锁上的阻塞线程都会变成可运行状态，第一个变为运行的线程就可以对互斥量加锁，其他线程就会看到互斥量依然是锁着的，只能回去再次等待它重新变为可用。</li></ul> <h2 id="_3-2-函数"><a href="#_3-2-函数" class="header-anchor">#</a> 3.2 函数</h2> <blockquote><p>互斥量是用pthread_mutex_t数据类型表示的。使用前必须先初始化</p></blockquote> <h3 id="初始化"><a href="#初始化" class="header-anchor">#</a> 初始化</h3> <p>两种初始化方式：</p> <ol><li>设置为常量：PTHREAD_MUTEX_INITIALIZER（只适用于静态分配）</li> <li>调用函数初始化：调用pthread_mutex_init函数初始化，如果动态分配互斥量（例如：通过调用malloc函数）在释放内存前需要调用pthread_mutex_destroy。</li></ol> <div class="language-CPP line-numbers-mode"><pre class="language-cpp"><code><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;pthread.h&gt;</span></span>
<span class="token keyword">int</span> <span class="token function">pthread_mutex_init</span><span class="token punctuation">(</span>pthread_mutex_t <span class="token operator">*</span>restrict mutex<span class="token punctuation">,</span><span class="token keyword">const</span> pthread_mutexarrt_t <span class="token operator">*</span>restrict attr<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">int</span> <span class="token function">pthread_mutex_destroy</span><span class="token punctuation">(</span>pthread_mutex_t <span class="token operator">*</span>mutex<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br></div></div><ul><li>返回值：成功返回0，失败返回错误编号。</li> <li>若使用默认属性初始化互斥量，attr设为NULL即可。</li></ul> <h3 id="加锁、解锁"><a href="#加锁、解锁" class="header-anchor">#</a> 加锁、解锁</h3> <div class="language-CPP line-numbers-mode"><pre class="language-cpp"><code><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;pthread.h&gt;</span></span>
<span class="token comment">//加锁</span>
<span class="token keyword">int</span> <span class="token function">pthread_mutex_lock</span><span class="token punctuation">(</span>pthread_mutex_t <span class="token operator">*</span>mutex<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">//尝试加锁</span>
<span class="token keyword">int</span> <span class="token function">pthread_mutex_trylock</span><span class="token punctuation">(</span>pthread_mutex_t <span class="token operator">*</span>mutex<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">//解锁（上述两种加锁都用这个解锁）</span>
<span class="token keyword">int</span> <span class="token function">pthread_mutex_unlock</span><span class="token punctuation">(</span>pthread_mutex_t <span class="token operator">*</span>mutex<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br></div></div><ul><li>返回值：成功为0，失败为错误编号。</li> <li>若线程不希望被阻塞，可用pthread_mutex_trylock尝试对互斥量加锁。
<ul><li>互斥量处于未锁住状态，那么pthread_mutex_trylock将锁住互斥量，不会出现阻塞直接返回0。</li> <li>互斥量处于锁住状态，那么pthread_mutex_trylock将会失败，不能锁住互斥量，返回ebusy。</li></ul></li></ul> <h2 id="_3-3-来个栗子"><a href="#_3-3-来个栗子" class="header-anchor">#</a> 3.3 来个栗子</h2> <p><strong>目的：使用互斥量保护数据结构</strong></p> <p>当一个以上的线程需要访问动态分配的对象时，我们可以在对象中嵌入引用计数，确保在所有使用该对象的线程完成数据访问之前，该对象内存空间不会被释放。</p> <ul><li>在对引用计数加 1、减 1、检查引用计数是否到达 0 这些操作之前需要锁住互斥量。</li> <li>在foo_alloc 函数中将引用计数初始化为 1 时没必要加锁，因为在这个操作之前分配线程是唯一引用该对象的线程。</li> <li>但是在这之后如果要将该对象放到一个列表中，那么它就有可能被别的线程发现，这时候需要首先对它加锁。</li> <li>在使用该对象前，线程需要调用foo_hold对这个对象的引用计数加1。</li> <li>当对象使用完毕时，必须调用foo_rele释放引用。最后一个引用被释放时，对象所占的内存空间就被释放。</li> <li>在这个例子中，我们忽略了线程在调用foo_hold之前是如何找到对象的。如果有另一个线程在调用foo_hold时阻塞等待互斥锁，这时即使该对象引用计数为0，foo_rele释放该对象的内存仍然是不对的。可以通过确保对象在释放内存前不会被找到这种方式来避免上述问题。可以通过下面的例子来看看如何做到这一点。</li></ul> <div class="language-CPP line-numbers-mode"><pre class="language-cpp"><code><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;stdlib.h&gt;</span>        <span class="token comment">// 包含标准库，用于内存管理函数。</span></span>
<span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;pthread.h&gt;</span>       <span class="token comment">// 包含pthread库，用于多线程支持。</span></span>

<span class="token keyword">struct</span> <span class="token class-name">foo</span> <span class="token punctuation">{</span>
    <span class="token keyword">int</span> 			f_count<span class="token punctuation">;</span>  <span class="token comment">// 一个整数字段，用于存储结构体的引用计数。</span>
    pthread_mutex_t f_lock<span class="token punctuation">;</span>   <span class="token comment">// 用于控制对结构体的访问的互斥锁。</span>
    <span class="token keyword">int</span> 			f_id<span class="token punctuation">;</span>     <span class="token comment">// 一个整数字段，用于存储结构体的标识符。</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token comment">// 分配并初始化“foo”结构体的函数</span>
<span class="token keyword">struct</span> <span class="token class-name">foo</span><span class="token operator">*</span> <span class="token function">foo_alloc</span><span class="token punctuation">(</span><span class="token keyword">int</span> id<span class="token punctuation">)</span> <span class="token punctuation">{</span> 
    <span class="token keyword">struct</span> <span class="token class-name">foo</span><span class="token operator">*</span> fp<span class="token punctuation">;</span>           	<span class="token comment">// 声明一个指向“foo”结构体的指针。</span>
    <span class="token keyword">if</span><span class="token punctuation">(</span><span class="token punctuation">(</span>fp <span class="token operator">=</span> <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token keyword">sizeof</span><span class="token punctuation">(</span><span class="token keyword">struct</span> <span class="token class-name">foo</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">!=</span> <span class="token constant">NULL</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 为结构体分配内存。</span>
        fp<span class="token operator">-&gt;</span>f_count <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>       						<span class="token comment">// 将引用计数初始化为1。</span>
        fp<span class="token operator">-&gt;</span>f_id <span class="token operator">=</span> id<span class="token punctuation">;</span>         						<span class="token comment">// 使用提供的ID初始化结构体的ID。</span>
        <span class="token keyword">if</span><span class="token punctuation">(</span><span class="token function">pthread_mutex_init</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>fp<span class="token operator">-&gt;</span>f_lock<span class="token punctuation">,</span> <span class="token constant">NULL</span><span class="token punctuation">)</span> <span class="token operator">!=</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 初始化互斥锁。</span>
            <span class="token function">free</span><span class="token punctuation">(</span>fp<span class="token punctuation">)</span><span class="token punctuation">;</span>           					<span class="token comment">// 如果互斥锁初始化失败，释放内存。</span>
            <span class="token keyword">return</span><span class="token punctuation">(</span><span class="token constant">NULL</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       					<span class="token comment">// 返回NULL以指示失败。</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">return</span><span class="token punctuation">(</span>fp<span class="token punctuation">)</span><span class="token punctuation">;</span>                <span class="token comment">// 返回初始化的“foo”结构体。</span>
<span class="token punctuation">}</span>
<span class="token comment">// 增加“foo”结构体的引用计数的函数</span>
<span class="token keyword">void</span> <span class="token function">foo_hold</span><span class="token punctuation">(</span><span class="token keyword">struct</span> <span class="token class-name">foo</span><span class="token operator">*</span> fp<span class="token punctuation">)</span> <span class="token punctuation">{</span>  
    <span class="token function">pthread_mutex_lock</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>fp<span class="token operator">-&gt;</span>f_lock<span class="token punctuation">)</span><span class="token punctuation">;</span> 	<span class="token comment">// 获取互斥锁以确保独占访问。</span>
    fp<span class="token operator">-&gt;</span>f_count<span class="token operator">++</span><span class="token punctuation">;</span>            			<span class="token comment">// 增加引用计数。</span>
    <span class="token function">pthread_mutex_unlock</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>fp<span class="token operator">-&gt;</span>f_lock<span class="token punctuation">)</span><span class="token punctuation">;</span> 	<span class="token comment">// 释放互斥锁。</span>
<span class="token punctuation">}</span>
<span class="token comment">// 释放“foo”结构体并可能释放其内存的函数</span>
<span class="token keyword">void</span> <span class="token function">foo_rele</span><span class="token punctuation">(</span><span class="token keyword">struct</span> <span class="token class-name">foo</span><span class="token operator">*</span> fp<span class="token punctuation">)</span> <span class="token punctuation">{</span>  
    <span class="token function">pthread_mutex_lock</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>fp<span class="token operator">-&gt;</span>f_lock<span class="token punctuation">)</span><span class="token punctuation">;</span> 		<span class="token comment">// 获取互斥锁以确保独占访问。</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">--</span>fp<span class="token operator">-&gt;</span>f_count <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> 				<span class="token comment">// 如果引用计数减为0，则可以安全地释放结构体。</span>
        <span class="token function">pthread_mutex_unlock</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>fp<span class="token operator">-&gt;</span>f_lock<span class="token punctuation">)</span><span class="token punctuation">;</span> 	<span class="token comment">// 释放互斥锁。</span>
        <span class="token function">pthread_mutex_destory</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>fp<span class="token operator">-&gt;</span>f_lock<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 销毁互斥锁。</span>
        <span class="token function">free</span><span class="token punctuation">(</span>fp<span class="token punctuation">)</span><span class="token punctuation">;</span>           				<span class="token comment">// 释放与“foo”结构体关联的内存。</span>
    <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
        <span class="token function">pthread_mutex_unlock</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>fp<span class="token operator">-&gt;</span>f_lock<span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token comment">// 如果引用计数不为零，释放互斥锁。</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br><span class="line-number">33</span><br><span class="line-number">34</span><br><span class="line-number">35</span><br><span class="line-number">36</span><br><span class="line-number">37</span><br><span class="line-number">38</span><br></div></div><h1 id="_4、-条件变量"><a href="#_4、-条件变量" class="header-anchor">#</a> 4、 条件变量</h1> <h2 id="_4-1-理解"><a href="#_4-1-理解" class="header-anchor">#</a> 4.1 理解</h2> <p>条件变量是线程可用的另一种同步机制。条件变量给多个线程提供了一个会合的场所。条件变量与互斥量一起使用时，允许线程以无竞争的方式等待特定的条件发生。</p> <p>条件本身是由互斥量保护的。线程在改变条件状态之前必须首先锁住互斥量。其他线程在获得互斥量之前不会察觉到这种改变，因为互斥量必须在锁定以后才能计算条件。</p> <h2 id="_4-2-函数"><a href="#_4-2-函数" class="header-anchor">#</a> 4.2 函数</h2> <h3 id="初始化-2"><a href="#初始化-2" class="header-anchor">#</a> 初始化</h3> <p>由pthread_cond_t数据类型表示</p> <p>两种初始化方式：</p> <ul><li>静态分配：把常量PTHREAD_COND_INITIALIZER赋给静态分配的条件变量</li> <li>动态分配：需要使用pthread_cond_init函数对它进行初始化</li></ul> <p>在释放条件变量底层的内存空间之前，可以使用pthread_cond_destroy函数对条件变量进行反初始化（deinitialize）</p> <div class="language-CPP line-numbers-mode"><pre class="language-cpp"><code><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;pthread.h&gt;</span></span>
<span class="token keyword">int</span> <span class="token function">pthread_cond_init</span><span class="token punctuation">(</span>pthread_cond_t <span class="token operator">*</span>restrict cond<span class="token punctuation">,</span> <span class="token keyword">const</span> pthread_condattr_t <span class="token operator">*</span>restrict attr<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">int</span> <span class="token function">pthread_cond_destroy</span><span class="token punctuation">(</span>pthread_cond_t <span class="token operator">*</span>cond<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br></div></div><p>两个函数的返回值：若成功，返回0；否则，返回错误编号</p> <p>除非需要创建一个具有非默认属性的条件变量，否则pthread_cond_init函数的attr参数可以设置为NULL。</p> <p>我们使用pthread_cond_wait等待条件变量变为真。如果在给定的时间内条件不能满足，那么会生成一个返回错误码的变量。</p> <div class="language-CPP line-numbers-mode"><pre class="language-cpp"><code><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;pthread.h&gt;</span></span>
<span class="token keyword">int</span> <span class="token function">pthread_cond_wait</span><span class="token punctuation">(</span>pthread_cond_t <span class="token operator">*</span>restrict cond<span class="token punctuation">,</span> pthread_mutex_t <span class="token operator">*</span>restrict mutex<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">int</span> <span class="token function">pthread_cond_timedwait</span><span class="token punctuation">(</span>pthread_cond_t <span class="token operator">*</span> restrict cond<span class="token punctuation">,</span>pthread_mutex_t <span class="token operator">*</span>restrict mutex<span class="token punctuation">,</span><span class="token keyword">const</span> <span class="token keyword">struct</span> <span class="token class-name">timespaec</span> <span class="token operator">*</span>restrict tsptr<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br></div></div><p>两个函数的返回值：若成功，返回0；否则，返回错误编号</p> <h3 id="注意事项"><a href="#注意事项" class="header-anchor">#</a> 注意事项</h3> <ul><li>传递给pthread_cond_wait的互斥量对条件进行保护。</li> <li>调用者把锁住的互斥量传给函数，函数然后自动把调用线程放到等待条件的线程列表上，对互斥量解锁。这就关闭了条件检查和线程进入休眠状态等待条件改变这两个操作之间的时间通道，这样线程就不会错过条件的任何变化。</li> <li>pthread_cond_wait返回时，互斥量再次被锁住。</li> <li>pthread_cond_timedwait函数的功能与pthread_cond_wait函数相似，只是多了一个超时（tsptr）。超时值指定了我们愿意等待多长时间，它是通过timespec结构指定的。这个时间值是一个绝对数而不是相对数。</li> <li>例如，假设愿意等待3分钟。那么，并不是把3分钟转换成timespec结构，而是需要把当前时间加上3分钟再转换成timespec结构。</li> <li>可以使用clock_gettime函数（见6.10节）获取timespec结构表示的当前时间。但是目前并不是所有的平台都支持这个函数，因此，也可以用另一个函数gettimeofday 获取timeval结构表示的当前时间，然后把这个时间转换成timespec结构。</li></ul> <div class="language-CPP line-numbers-mode"><pre class="language-cpp"><code><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;sys/time.h&gt;</span></span>
<span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;stdlib.h&gt;</span></span>
<span class="token keyword">void</span> <span class="token function">maketimeout</span><span class="token punctuation">(</span><span class="token keyword">struct</span> <span class="token class-name">timespec</span> <span class="token operator">*</span>tsp<span class="token punctuation">,</span> <span class="token keyword">long</span> minutes<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">struct</span> <span class="token class-name">timeval</span> now<span class="token punctuation">;</span>
    <span class="token function">gettimeofday</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>now<span class="token punctuation">,</span><span class="token constant">NULL</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    tsp<span class="token operator">-&gt;</span>tv_sec <span class="token operator">=</span> now<span class="token punctuation">.</span>tv_sec<span class="token punctuation">;</span>
    tsp<span class="token operator">-&gt;</span>tv_nsec <span class="token operator">=</span> now<span class="token punctuation">.</span>tv_usec<span class="token operator">*</span><span class="token number">1000</span><span class="token punctuation">;</span>
    tsp<span class="token operator">-&gt;</span>tv_sec <span class="token operator">+=</span> minutes<span class="token operator">*</span><span class="token number">60</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br></div></div><p>如果超时到期时条件还是没有出现，pthread_cond_timewait 将重新获取互斥量，然后返回错误ETIMEDOUT。从pthread_cond_wait或者pthread_cond_timedwait调用成功返回时，线程需要重新计算条件，因为另一个线程可能已经在运行并改变了条件。</p> <p>有两个函数可以用于通知线程条件已经满足。pthread_cond_signal函数至少能唤醒一个等待该条件的线程，而pthread_cond_broadcast函数则能唤醒等待该条件的所有线程。</p> <p>POSIX 规范为了简化 pthread_cond_signal 的实现，允许它在实现的时候唤醒一个以上的线程。</p> <div class="language-CPP line-numbers-mode"><pre class="language-cpp"><code><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;pthread.h&gt;</span></span>
<span class="token keyword">int</span> <span class="token function">pthread_cond_signal</span><span class="token punctuation">(</span>pthread_cond_t <span class="token operator">*</span>cond<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">int</span> <span class="token function">pthread_cond_broadcast</span><span class="token punctuation">(</span>pthread_cond_t <span class="token operator">*</span>cond<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br></div></div><p>两个函数的返回值：若成功，返回0；否则，返回错误编号</p> <p>在调用pthread_cond_signal或者pthread_cond_broadcast时，我们说这是在给线程或者条件发信号。必须注意，一定要在改变条件状态以后再给线程发信号。</p> <h2 id="_4-3-举个例子"><a href="#_4-3-举个例子" class="header-anchor">#</a> 4.3 举个例子</h2> <p>以下例子给出了如何结合使用条件变量和互斥量对线程进行同步。</p> <div class="language-CPP line-numbers-mode"><pre class="language-cpp"><code><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;pthread.h&gt;</span></span>

<span class="token comment">// 定义消息结构体</span>
<span class="token keyword">struct</span> <span class="token class-name">msg</span> <span class="token punctuation">{</span>
    <span class="token keyword">struct</span> <span class="token class-name">msg</span><span class="token operator">*</span> m_next<span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">struct</span> <span class="token class-name">msg</span><span class="token operator">*</span> workq<span class="token punctuation">;</span> <span class="token comment">// 全局消息队列指针</span>

pthread_cond_t qready <span class="token operator">=</span> PTHREAD_COND_INITIALIZER<span class="token punctuation">;</span> <span class="token comment">// 条件变量，用于通知等待中的线程</span>
pthread_mutex_t qlock <span class="token operator">=</span> PTHREAD_MUTEX_INITIALIZER<span class="token punctuation">;</span> <span class="token comment">// 互斥锁，用于保护消息队列</span>

<span class="token keyword">void</span> <span class="token function">process_msg</span><span class="token punctuation">(</span><span class="token keyword">void</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">struct</span> <span class="token class-name">msg</span><span class="token operator">*</span> mp<span class="token punctuation">;</span>
    <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token punctuation">;</span><span class="token punctuation">;</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token function">pthread_mutex_lock</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>qlock<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 获取互斥锁，保护对消息队列的访问</span>
        <span class="token comment">// 如果消息队列为空，等待条件变量</span>
        <span class="token keyword">while</span> <span class="token punctuation">(</span>workq <span class="token operator">==</span> <span class="token constant">NULL</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token function">pthread_cond_wait</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>qready<span class="token punctuation">,</span> <span class="token operator">&amp;</span>qlock<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
        mp <span class="token operator">=</span> workq<span class="token punctuation">;</span> <span class="token comment">// 获取队列头部的消息</span>
        workq <span class="token operator">=</span> mp<span class="token operator">-&gt;</span>m_next<span class="token punctuation">;</span> <span class="token comment">// 更新队列头部指针，将头部消息出队</span>
        <span class="token function">pthread_mutex_unlock</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>qlock<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 释放互斥锁，允许其他线程访问消息队列</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">void</span> <span class="token function">enqueue_msg</span><span class="token punctuation">(</span><span class="token keyword">struct</span> <span class="token class-name">msg</span><span class="token operator">*</span> mp<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token function">pthread_mutex_lock</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>qlock<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 获取互斥锁，保护对消息队列的访问</span>
    mp<span class="token operator">-&gt;</span>m_next <span class="token operator">=</span> workq<span class="token punctuation">;</span> <span class="token comment">// 将新消息插入到队列头部</span>
    workq <span class="token operator">=</span> mp<span class="token punctuation">;</span> <span class="token comment">// 更新队列头部指针，将新消息设置为队列头部</span>
    <span class="token function">pthread_mutex_unlock</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>qlock<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 释放互斥锁，允许其他线程访问消息队列</span>
    <span class="token function">pthread_cond_signal</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>qready<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 发送信号，通知等待中的消费者线程有新消息可处理</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br><span class="line-number">33</span><br></div></div><ul><li>条件是工作队列的状态。我们用互斥量保护条件，在 while 循环中判断条件。</li> <li>把消息放到工作队列时，需要占有互斥量，但在给等待线程发信号时，不需要占有互斥量。</li> <li>只要线程在调用pthread_cond_signal之前把消息从队列中拖出了，就可以在释放互斥量以后完成这部分工作。</li> <li>因为我们是在 while 循环中检查条件，所以不存在这样的问题：线程醒来，发现队列仍为空，然后返回继续等待。如果代码不能容忍这种竞争，就需要在给线程发信号的时候占有互斥量。</li></ul> <h1 id="_5、-读写锁"><a href="#_5、-读写锁" class="header-anchor">#</a> 5、 读写锁</h1> <h2 id="_5-1-引言"><a href="#_5-1-引言" class="header-anchor">#</a> 5.1 引言</h2> <p>读写锁（reader-writer lock）与互斥量类似，不过读写锁允许更高的并行性。</p> <p><strong>互斥锁只有两个状态：（一次只能由一个线程可以对其加锁）</strong></p> <ul><li>锁住状态</li> <li>不加锁状态</li></ul> <p><strong>读写锁可以有3中状态：（一次只有一个线程可以占有写模式的读写锁，但是多个线程可以同时占有读模式的读写锁）</strong></p> <ul><li>读模式的加锁状态</li> <li>写模式的加锁状态</li> <li>不加锁状态</li></ul> <p><strong>注意：</strong></p> <ol><li>写加锁状态：解锁前，所有试图加锁的线程都会被阻塞。</li> <li>读加锁状态：所有试图以读模式加锁的线程都可以得到访问权，但是，试图以写模式加锁的线程都会被阻塞，直到所有的线程释放它们的读锁为止。</li> <li>读写锁非常适合于对数据结构读的次数远大于写的情况。</li></ol> <p>读写锁也叫做共享互斥锁（shared-exclusive lock）。当读写锁是读模式锁住时，就可以说成是以共享模式锁住的。当它是写模式锁住的时候，就可以说成是以互斥模式锁住的。</p> <p>与互斥量相比，读写锁在使用之前必须初始化，在释放它们底层的内存之前必须销毁。</p> <h2 id="_5-2-函数"><a href="#_5-2-函数" class="header-anchor">#</a> 5.2 函数</h2> <div class="language-CPP line-numbers-mode"><pre class="language-cpp"><code><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;pthread.h&gt;</span></span>
<span class="token keyword">int</span> <span class="token function">pthread_rwlock_init</span><span class="token punctuation">(</span>pthread_rwlock_t<span class="token operator">*</span> restrict rwlock<span class="token punctuation">,</span> <span class="token keyword">const</span> pthread_rwlockattr_t<span class="token operator">*</span> restrict attr<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">int</span> <span class="token function">pthread_rwlock_destroy</span><span class="token punctuation">(</span>pthread_rwlock_t<span class="token operator">*</span> rwlock<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br></div></div><ul><li>返回值：成功为0，失败为错误编号</li> <li>若使用读写锁默认属性，传一个NULL指针给attr</li></ul> <h3 id="初始化-3"><a href="#初始化-3" class="header-anchor">#</a> 初始化</h3> <p>两种初始化：</p> <ul><li>设置为常量：如果默认属性够用，可用静态分配的读写锁进行初始化。PTHREAD_RWLOCK_INITIALIZER常量</li> <li>调用函数：pthread_rwlock_init</li></ul> <p>如果pthread_rwlock_init为读写锁分配了资源，pthread_rwlock_destroy将释放这些资源。</p> <p>在释放读写锁占用的内存之前，需要调用pthread_rwlock_destroy做清理工作。如果在调用pthread_rwlock_destroy之前就释放了读写锁占用的内存空间，那么分配给这个锁的资源就会丢失。</p> <h3 id="加锁、解锁-2"><a href="#加锁、解锁-2" class="header-anchor">#</a> 加锁、解锁</h3> <div class="language-CPP line-numbers-mode"><pre class="language-cpp"><code><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;pthread.h&gt;</span></span>
<span class="token keyword">int</span> <span class="token function">pthread_rwlock_rdlock</span><span class="token punctuation">(</span>pthread_rwlock_t <span class="token operator">*</span>rwlock<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">int</span> <span class="token function">pthread_rwlock_wrlock</span><span class="token punctuation">(</span>pthread_rwlock_t <span class="token operator">*</span>rwlock<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">int</span> <span class="token function">pthread_rwlock_unlock</span><span class="token punctuation">(</span>pthradd_rwlock_t <span class="token operator">*</span>rwlock<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br></div></div><ul><li>返回值：成功为0，失败为错误编号</li> <li>各种实现可能会对共享模式下可获取的读写锁的次数进行限制，<strong>所以需要检查pthread_rwlock_rdlock的返回值</strong>。</li></ul> <p><strong>==Single UNIX Specification还定义了读写锁原语的条件版本。==</strong></p> <div class="language-CPP line-numbers-mode"><pre class="language-cpp"><code><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;pthread.h&gt;</span></span>
<span class="token keyword">int</span> <span class="token function">pthread_rwlock_tryrdlock</span><span class="token punctuation">(</span>pthread_rwlock_t <span class="token operator">*</span>rwlock<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">int</span> <span class="token function">pthread_rwlock_trywrlock</span><span class="token punctuation">(</span>pthread_rwlock_t <span class="token operator">*</span>rwlock<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br></div></div><p>返回值：成功为0，失败返回错误编号EBUSY</p> <h2 id="_5-3-举个栗子"><a href="#_5-3-举个栗子" class="header-anchor">#</a> 5.3 举个栗子</h2> <p>目的：解释读写锁的使用。作业请求队列由单个读写锁保护。这个例子给出了多个工作线程获取单个主线程分配给它们的作业。</p> <div class="language-CPP line-numbers-mode"><pre class="language-cpp"><code><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;stdlib.h&gt;</span></span>
<span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;pthread.h&gt;</span></span>
<span class="token comment">// 定义任务结构体</span>
<span class="token keyword">struct</span> <span class="token class-name">job</span> <span class="token punctuation">{</span> 
    <span class="token keyword">struct</span> <span class="token class-name">job</span> <span class="token operator">*</span>j_next<span class="token punctuation">;</span> 	<span class="token comment">// 指向下一个任务</span>
    <span class="token keyword">struct</span> <span class="token class-name">job</span> <span class="token operator">*</span>j_prev<span class="token punctuation">;</span> 	<span class="token comment">// 指向前一个任务</span>
    pthread_t 	j_id<span class="token punctuation">;</span> 		<span class="token comment">// 任务的线程ID</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token comment">// 定义队列结构体</span>
<span class="token keyword">struct</span> <span class="token class-name">queue</span> <span class="token punctuation">{</span> 
    <span class="token keyword">struct</span> <span class="token class-name">job</span> <span class="token operator">*</span>q_head<span class="token punctuation">;</span> 		<span class="token comment">// 指向队列头部的任务</span>
    <span class="token keyword">struct</span> <span class="token class-name">job</span> <span class="token operator">*</span>q_tail<span class="token punctuation">;</span> 		<span class="token comment">// 指向队列尾部的任务</span>
    pthread_rwlock_t q_lock<span class="token punctuation">;</span>	<span class="token comment">// 读写锁</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token comment">// 初始化队列</span>
<span class="token keyword">int</span> <span class="token function">queue_init</span><span class="token punctuation">(</span><span class="token keyword">struct</span> <span class="token class-name">queue</span> <span class="token operator">*</span>qp<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">int</span> err<span class="token punctuation">;</span> <span class="token comment">// 错误码</span>
    qp<span class="token operator">-&gt;</span>q_head <span class="token operator">=</span> <span class="token constant">NULL</span><span class="token punctuation">;</span>  <span class="token comment">// 队列头部置空</span>
    qp<span class="token operator">-&gt;</span>q_tail <span class="token operator">=</span> <span class="token constant">NULL</span><span class="token punctuation">;</span>  <span class="token comment">// 队列尾部置空</span>
    err <span class="token operator">=</span> <span class="token function">pthread_rwlock_init</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>qp<span class="token operator">-&gt;</span>q_lock<span class="token punctuation">,</span><span class="token constant">NULL</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 初始化读写锁</span>
    <span class="token keyword">if</span><span class="token punctuation">(</span>err <span class="token operator">!=</span> <span class="token number">0</span><span class="token punctuation">)</span> 		<span class="token comment">// 如果出现错误</span>
        <span class="token keyword">return</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span><span class="token punctuation">;</span> 	<span class="token comment">// 返回错误码</span>
    <span class="token keyword">return</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 返回成功</span>
<span class="token punctuation">}</span>
<span class="token comment">// 向队列头部插入任务(头插法)</span>
<span class="token keyword">void</span> <span class="token function">job_insert</span><span class="token punctuation">(</span><span class="token keyword">struct</span> <span class="token class-name">queue</span> <span class="token operator">*</span>qp<span class="token punctuation">,</span><span class="token keyword">struct</span> <span class="token class-name">job</span> <span class="token operator">*</span>jp<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token function">pthread_rwlock_wrlock</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>qp<span class="token operator">-&gt;</span>q_lock<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 写锁定</span>
    jp<span class="token operator">-&gt;</span>j_next <span class="token operator">=</span> qp<span class="token operator">-&gt;</span>q_head<span class="token punctuation">;</span> <span class="token comment">// 新任务的下一个指向当前头部任务</span>
    jp<span class="token operator">-&gt;</span>j_prev <span class="token operator">=</span> <span class="token constant">NULL</span><span class="token punctuation">;</span>		 <span class="token comment">// 新任务的前一个置空</span>
    <span class="token keyword">if</span><span class="token punctuation">(</span>qp<span class="token operator">-&gt;</span>q_head <span class="token operator">!=</span> <span class="token constant">NULL</span><span class="token punctuation">)</span> 	 <span class="token comment">// 如果队列不为空</span>
        qp<span class="token operator">-&gt;</span>q_head<span class="token operator">-&gt;</span>j_prev <span class="token operator">=</span> jp<span class="token punctuation">;</span> <span class="token comment">// 当前头部任务的前一个指向新任务</span>
    <span class="token keyword">else</span> 					 <span class="token comment">// 如果队列为空</span>
        qp<span class="token operator">-&gt;</span>q_tail <span class="token operator">=</span> jp<span class="token punctuation">;</span> 	 <span class="token comment">// 队列尾部指向新任务</span>
    qp<span class="token operator">-&gt;</span>q_head <span class="token operator">=</span> jp<span class="token punctuation">;</span> 		 <span class="token comment">// 队列头部指向新任务</span>
    <span class="token function">pthread_rwlock_unlock</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>qp<span class="token operator">-&gt;</span>q_lock<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 解锁</span>
<span class="token punctuation">}</span>
<span class="token comment">// 向队列尾部追加任务</span>
<span class="token keyword">void</span> <span class="token function">job_append</span><span class="token punctuation">(</span><span class="token keyword">struct</span> <span class="token class-name">queue</span> <span class="token operator">*</span>qp<span class="token punctuation">,</span> <span class="token keyword">struct</span> <span class="token class-name">job</span> <span class="token operator">*</span>jp<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token function">pthread_rwlock_wrlock</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>qp<span class="token operator">-&gt;</span>q_lock<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 写锁定</span>
    jp<span class="token operator">-&gt;</span>j_next <span class="token operator">=</span> <span class="token constant">NULL</span><span class="token punctuation">;</span> 					<span class="token comment">// 新任务的下一个置空</span>
    jp<span class="token operator">-&gt;</span>j_prev <span class="token operator">=</span> qp<span class="token operator">-&gt;</span>q_tail<span class="token punctuation">;</span> 			<span class="token comment">// 新任务的前一个指向当前尾部任务</span>
    <span class="token keyword">if</span><span class="token punctuation">(</span>qp<span class="token operator">-&gt;</span>q_tail <span class="token operator">!=</span> <span class="token constant">NULL</span><span class="token punctuation">)</span> 				<span class="token comment">// 如果队列尾部不为空</span>
        qp<span class="token operator">-&gt;</span>q_tail<span class="token operator">-&gt;</span>j_next <span class="token operator">=</span> jp<span class="token punctuation">;</span> 		<span class="token comment">// 当前尾部任务的下一个指向新任务</span>
    <span class="token keyword">else</span> 								<span class="token comment">// 如果队列尾部为空</span>
        qp<span class="token operator">-&gt;</span>q_head <span class="token operator">=</span> jp<span class="token punctuation">;</span> 				<span class="token comment">// 队列头部指向新任务</span>
    qp<span class="token operator">-&gt;</span>q_tail <span class="token operator">=</span> jp<span class="token punctuation">;</span> 					<span class="token comment">// 队列尾部指向新任务</span>
    <span class="token function">pthread_rwlock_unlock</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>qp<span class="token operator">-&gt;</span>q_lock<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 解锁</span>
<span class="token punctuation">}</span>
<span class="token comment">// 从队列中移除任务</span>
<span class="token keyword">void</span> <span class="token function">job_remove</span><span class="token punctuation">(</span><span class="token keyword">struct</span> <span class="token class-name">queue</span> <span class="token operator">*</span>qp<span class="token punctuation">,</span> <span class="token keyword">struct</span> <span class="token class-name">job</span> <span class="token operator">*</span>jp<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token function">pthread_rwlock_wrlock</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>qp<span class="token operator">-&gt;</span>q_lock<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 写锁定</span>
    <span class="token keyword">if</span><span class="token punctuation">(</span>jp <span class="token operator">==</span> qp<span class="token operator">-&gt;</span>q_head<span class="token punctuation">)</span> <span class="token punctuation">{</span> 				<span class="token comment">// 如果要移除的任务是队列头部任务</span>
        qp<span class="token operator">-&gt;</span>q_head <span class="token operator">=</span> jp<span class="token operator">-&gt;</span>j_next<span class="token punctuation">;</span> 		<span class="token comment">// 队列头部指向下一个任务</span>
        <span class="token keyword">if</span><span class="token punctuation">(</span>qp<span class="token operator">-&gt;</span>q_tail <span class="token operator">==</span> jp<span class="token punctuation">)</span> 			<span class="token comment">// 如果队列尾部也是要移除的任务</span>
            qp<span class="token operator">-&gt;</span>q_tail <span class="token operator">=</span> <span class="token constant">NULL</span><span class="token punctuation">;</span> 			<span class="token comment">// 队列尾部置空</span>
        <span class="token keyword">else</span> 							<span class="token comment">// 如果队列尾部不是要移除的任务</span>
            jp<span class="token operator">-&gt;</span>j_next<span class="token operator">-&gt;</span>j_prev <span class="token operator">=</span> jp<span class="token operator">-&gt;</span>j_prev<span class="token punctuation">;</span> <span class="token comment">// 下一个任务的前一个指向要移除任务的前一个</span>
    <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span><span class="token punctuation">(</span>jp <span class="token operator">==</span> qp<span class="token operator">-&gt;</span>q_tail<span class="token punctuation">)</span> <span class="token punctuation">{</span> 		<span class="token comment">// 如果要移除的任务是队列尾部任务</span>
        qp<span class="token operator">-&gt;</span>q_tail <span class="token operator">=</span> jp<span class="token operator">-&gt;</span>j_prev<span class="token punctuation">;</span> 		<span class="token comment">// 队列尾部指向前一个任务</span>
        jp<span class="token operator">-&gt;</span>j_prev<span class="token operator">-&gt;</span>j_next <span class="token operator">=</span> jp<span class="token operator">-&gt;</span>j_next<span class="token punctuation">;</span><span class="token comment">// 前一个任务的下一个指向要移除任务的下一个</span>
    <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token comment">// 如果要移除的任务在队列中间</span>
        jp<span class="token operator">-&gt;</span>j_prev<span class="token operator">-&gt;</span>j_next <span class="token operator">=</span> jp<span class="token operator">-&gt;</span>j_next<span class="token punctuation">;</span><span class="token comment">// 前一个任务的下一个指向要移除任务的下一个</span>
        jp<span class="token operator">-&gt;</span>j_next<span class="token operator">-&gt;</span>j_prev <span class="token operator">=</span> jp<span class="token operator">-&gt;</span>j_prev<span class="token punctuation">;</span><span class="token comment">// 下一个任务的前一个指向要移除任务的前一个</span>
    <span class="token punctuation">}</span>
    <span class="token function">pthread_rwlock_unlock</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>qp<span class="token operator">-&gt;</span>q_lock<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 解锁</span>
<span class="token punctuation">}</span>
<span class="token comment">// 查找队列中的任务</span>
<span class="token keyword">struct</span> <span class="token class-name">job</span> <span class="token operator">*</span><span class="token function">job_find</span><span class="token punctuation">(</span><span class="token keyword">struct</span> <span class="token class-name">queue</span> <span class="token operator">*</span>qp<span class="token punctuation">,</span> pthread_t id<span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token keyword">struct</span> <span class="token class-name">job</span><span class="token operator">*</span> jp<span class="token punctuation">;</span> 							<span class="token comment">// 任务指针</span>
    <span class="token keyword">if</span><span class="token punctuation">(</span><span class="token function">pthread_rwlock_rdlock</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>qp<span class="token operator">-&gt;</span>q_lock<span class="token punctuation">)</span> <span class="token operator">!=</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token comment">// 读锁定</span>
        <span class="token keyword">return</span><span class="token punctuation">(</span><span class="token constant">NULL</span><span class="token punctuation">)</span><span class="token punctuation">;</span> 							<span class="token comment">// 如果出现错误，返回空</span>
    <span class="token keyword">for</span><span class="token punctuation">(</span>jp <span class="token operator">=</span> qp<span class="token operator">-&gt;</span>q_head<span class="token punctuation">;</span> jp <span class="token operator">!=</span> <span class="token constant">NULL</span><span class="token punctuation">;</span> jp <span class="token operator">=</span> jp<span class="token operator">-&gt;</span>j_next<span class="token punctuation">)</span> <span class="token comment">// 遍历队列中的任务</span>
        <span class="token keyword">if</span><span class="token punctuation">(</span><span class="token function">pthread_equal</span><span class="token punctuation">(</span>jp<span class="token operator">-&gt;</span>j_id<span class="token punctuation">,</span> id<span class="token punctuation">)</span><span class="token punctuation">)</span> 		<span class="token comment">// 如果找到了目标任务</span>
            <span class="token keyword">break</span><span class="token punctuation">;</span> 								<span class="token comment">// 退出循环</span>
    <span class="token function">pthread_rwlock_unlock</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>qp<span class="token operator">-&gt;</span>q_lock<span class="token punctuation">)</span><span class="token punctuation">;</span> 		<span class="token comment">// 解锁</span>
    <span class="token keyword">return</span><span class="token punctuation">(</span>jp<span class="token punctuation">)</span><span class="token punctuation">;</span> 								<span class="token comment">// 返回找到的任务</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br><span class="line-number">33</span><br><span class="line-number">34</span><br><span class="line-number">35</span><br><span class="line-number">36</span><br><span class="line-number">37</span><br><span class="line-number">38</span><br><span class="line-number">39</span><br><span class="line-number">40</span><br><span class="line-number">41</span><br><span class="line-number">42</span><br><span class="line-number">43</span><br><span class="line-number">44</span><br><span class="line-number">45</span><br><span class="line-number">46</span><br><span class="line-number">47</span><br><span class="line-number">48</span><br><span class="line-number">49</span><br><span class="line-number">50</span><br><span class="line-number">51</span><br><span class="line-number">52</span><br><span class="line-number">53</span><br><span class="line-number">54</span><br><span class="line-number">55</span><br><span class="line-number">56</span><br><span class="line-number">57</span><br><span class="line-number">58</span><br><span class="line-number">59</span><br><span class="line-number">60</span><br><span class="line-number">61</span><br><span class="line-number">62</span><br><span class="line-number">63</span><br><span class="line-number">64</span><br><span class="line-number">65</span><br><span class="line-number">66</span><br><span class="line-number">67</span><br><span class="line-number">68</span><br><span class="line-number">69</span><br><span class="line-number">70</span><br><span class="line-number">71</span><br><span class="line-number">72</span><br><span class="line-number">73</span><br><span class="line-number">74</span><br><span class="line-number">75</span><br><span class="line-number">76</span><br><span class="line-number">77</span><br></div></div><ul><li>在这个例子中，凡是需要向队列中增加作业或者从队列中删除作业的时候，都采用了写模式来锁住队列的读写锁。</li> <li>不管何时搜索队列，都需要获取读模式下的锁，允许所有的工作线程并发地搜索队列。</li> <li>在这种情况下，只有在线程搜索作业的频率远远高于增加或删除作业时，使用读写锁才可能改善性能。</li> <li>工作线程只能从队列中读取与它们的线程 ID 匹配的作业。由于作业结构同一时间只能由一个线程使用，所以不需要额外的加锁。</li></ul> <h1 id="_6、自旋锁"><a href="#_6、自旋锁" class="header-anchor">#</a> 6、自旋锁</h1> <h2 id="_6-1-理解"><a href="#_6-1-理解" class="header-anchor">#</a> 6.1 理解</h2> <p>自旋锁与互斥量类似，但它不是通过休眠使进程阻塞，而是在获取锁之前
一直处于忙等（自旋）阻塞状态。所以当线程自旋等待锁变为可用时，CPU不能做其他的事情。这也是自旋锁只能够被持有一小段时间的原因。</p> <p>可用于以下情况：</p> <ul><li>锁被持有时间短，线程不希望再重新调度上花费太多的成本。</li> <li>自旋锁用在非抢占式内核中时是非常有用的：除了提供互斥机制以外，它们会阻塞中断，这样中断处理程序就不会让系统陷入死锁状态，因为它需要获取已被加锁的自选锁（把中断想成是另一种抢占）。在这种类型的内核中，中断处理程序不能休眠，因此它们能用的同步原语只能是自旋锁。</li></ul> <h2 id="_6-2-函数"><a href="#_6-2-函数" class="header-anchor">#</a> 6.2 函数</h2> <h3 id="初始化-4"><a href="#初始化-4" class="header-anchor">#</a> 初始化</h3> <div class="language-CPP line-numbers-mode"><pre class="language-cpp"><code><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;pthread.h&gt;</span></span>
<span class="token comment">//对自旋锁进行初始化</span>
<span class="token keyword">int</span> <span class="token function">pthread_spin_init</span><span class="token punctuation">(</span>pthread_spinlock_t<span class="token operator">*</span> lock<span class="token punctuation">,</span> <span class="token keyword">int</span> pshared<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">//销毁自旋锁</span>
<span class="token keyword">int</span> <span class="token function">pthread_spin_destory</span><span class="token punctuation">(</span>pthread_spinlock_t <span class="token operator">*</span>lock<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br></div></div><p>返回值：成功为0，失败为错误编号</p> <p>参数：</p> <ul><li>pshared参数表示进程共享属性，表明自旋锁是如何获取的。
<ul><li>PTHREAD_PROCESS_SHARED，则自旋锁能被可用访问锁底层内存的线程所获取，即便那些线程属于不同的进程。</li> <li>如果设为PTHREAD_PROCESS_PRIVATE，自旋锁就只能被初始化该锁的进程内部的线程所访问。</li></ul></li></ul> <h3 id="加锁、解锁-3"><a href="#加锁、解锁-3" class="header-anchor">#</a> 加锁、解锁</h3> <div class="language-CPP line-numbers-mode"><pre class="language-cpp"><code><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;pthread.h&gt;</span></span>
<span class="token comment">//加锁，获取锁之前一直自旋</span>
<span class="token keyword">int</span> <span class="token function">pthread_spin_lock</span><span class="token punctuation">(</span>pthread_spinlock_t <span class="token operator">*</span>lock<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">//尝试加锁，不能获取锁返回EBUSY错误（不能自旋）</span>
<span class="token keyword">int</span> <span class="token function">pthread_spin_trylock</span><span class="token punctuation">(</span>pthread_spinlock_t <span class="token operator">*</span>lock<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">//解锁</span>
<span class="token keyword">int</span> <span class="token function">pthread_spin_unlock</span><span class="token punctuation">(</span>pthread_spinlock_t <span class="token operator">*</span>lock<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br></div></div><p>返回值：成功为0，失败为错误编号</p> <p><strong>注意：</strong></p> <ul><li>如果自旋锁当前在解锁状态的话，pthread_spin_lock函数不要自旋就可以对它加锁。如果线程已经对它加锁了，结果就是未定义的。</li> <li>调用pthread_spin_lock会返回EDEADLK错误（或其他错误），或者调用可能会永久自旋。</li> <li>不管是pthread_spin_lock还是pthread_spin_trylock，返回值为0的话就表示自旋锁被加锁。</li> <li>不要调用在持有自旋锁情况下可能会进入休眠状态的函数。如果调用了这些函数，会浪费CPU资源，因为其他线程需要获取自旋锁需要等待的时间就延长了。</li></ul> <h1 id="_7、屏障"><a href="#_7、屏障" class="header-anchor">#</a> 7、屏障</h1> <h2 id="_7-1-引言"><a href="#_7-1-引言" class="header-anchor">#</a> 7.1 引言</h2> <p>屏障（barrier）是用户协调多个线程并行工作的同步机制。屏障允许每个线程等待，直到所有的合作线程都到达某一点，然后从该点继续执行。</p> <p>pthread_join函数就是一种屏障，允许一个线程等待，直到另一个线程退出。</p> <p>但是屏障对象的概念更广，它们允许任意数量的线程等待，直到所有的线程完成处理工作，而线程不需要退出。所有线程达到屏障后可以接着工作。</p> <h2 id="_7-2-函数"><a href="#_7-2-函数" class="header-anchor">#</a> 7.2 函数</h2> <h3 id="初始化-5"><a href="#初始化-5" class="header-anchor">#</a> 初始化</h3> <div class="language-CPP line-numbers-mode"><pre class="language-cpp"><code><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;pthread.h&gt;</span></span>
<span class="token keyword">int</span> <span class="token function">pthread_barrier_init</span><span class="token punctuation">(</span>pthread_barrier_t<span class="token operator">*</span> restrict barrier<span class="token punctuation">,</span> <span class="token keyword">const</span> pthread_barrierattr_t<span class="token operator">*</span> restrict attr<span class="token punctuation">,</span> <span class="token keyword">unsigned</span> <span class="token keyword">int</span> count<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">int</span> <span class="token function">pthread_barrier_destory</span><span class="token punctuation">(</span>pthread_barrier_t <span class="token operator">*</span>barrier<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br></div></div><p>返回值：成功为0，失败为错误编号</p> <p>参数：</p> <ul><li>count：在允许所有线程继续运行之前，必须到达屏障的线程数目。</li> <li>attr：设置为NULL，用默认属性初始化屏障</li></ul> <p>可以使用pthread_barrier_wait函数表明，线程已完成工作，准备等所有其他线程赶上来。</p> <div class="language-CPP line-numbers-mode"><pre class="language-cpp"><code><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;pthread.h&gt;</span></span>
<span class="token keyword">int</span> <span class="token function">pthread_barrier_wait</span><span class="token punctuation">(</span>pthread_barrier_t <span class="token operator">*</span>barrier<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br></div></div><p>返回值：</p> <ul><li>成功为0或者PTHREAD_BARRIER_SERIAL_THREAD</li> <li>失败返回错误编号</li></ul> <p>调用pthread_barrier_wait的线程在屏障计数（调用pthread_barrier_init时设定）未满足条件时，会进入休眠状态。</p> <p>如果该线程是最后一个调用pthread_barrier_wait的线程，就满足了屏障计数，所有的线程都被唤醒。</p> <p>对于一个任意线程，pthread_barrier_wait函数返回了PTHREAD_BARRIER_SERIAL_THREAD。剩下的线程看到的返回值是0。这使得一个线程可以作为主线程，它可以工作在其他所有线程已完成的工作结果上。</p> <p>一旦达到屏障计数值，而且线程处于非阻塞状态，屏障就可以被重用。</p> <p>但是除非在调用了pthread_barrier_destroy函数之后，又调用了pthread_barrier_init函数对计数用另外的数进行初始化，否则屏障计数不会改变。</p> <h2 id="_7-3-举个例子"><a href="#_7-3-举个例子" class="header-anchor">#</a> 7.3 举个例子</h2> <p>以下例子给出了在一个任务上合作的多个线程之间如何用屏障进行同步。</p> <div class="language-CPP line-numbers-mode"><pre class="language-cpp"><code><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&quot;apue.h&quot;</span>        </span>
<span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;pthread.h&gt;</span>     </span>
<span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;limits.h&gt;</span>      <span class="token comment">// 包含限制常量</span></span>
<span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;sys/time.h&gt;</span>    </span>
<span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">define</span> <span class="token macro-name">NTHR</span> <span class="token expression"><span class="token number">8</span>           </span><span class="token comment">// 定义线程数</span></span>
<span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">define</span> <span class="token macro-name">NUMNUM</span> <span class="token expression"><span class="token number">8000000L</span>  </span><span class="token comment">// 定义总元素数</span></span>
<span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">define</span> <span class="token macro-name">TNUM</span> <span class="token expression"><span class="token punctuation">(</span>NUMNUM<span class="token operator">/</span>NTHR<span class="token punctuation">)</span>  </span><span class="token comment">// 每个线程的元素数</span></span>

<span class="token keyword">long</span> nums<span class="token punctuation">[</span>NUMNUM<span class="token punctuation">]</span><span class="token punctuation">;</span>         <span class="token comment">// 存储随机数的数组</span>
<span class="token keyword">long</span> snums<span class="token punctuation">[</span>NUMNUM<span class="token punctuation">]</span><span class="token punctuation">;</span>        <span class="token comment">// 存储排序后的数值的数组</span>

pthread_barrier_t b<span class="token punctuation">;</span>      <span class="token comment">// 声明一个 pthread 屏障变量</span>

<span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">ifdef</span> <span class="token expression">SOLARIS</span></span>
<span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">define</span> <span class="token macro-name">headpsort</span> <span class="token expression">qsort   </span><span class="token comment">// 在 Solaris 平台上使用 qsort</span></span>
<span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">else</span></span>
<span class="token keyword">extern</span> <span class="token keyword">int</span> <span class="token function">heapsort</span><span class="token punctuation">(</span><span class="token keyword">void</span><span class="token operator">*</span><span class="token punctuation">,</span> size_t<span class="token punctuation">,</span> size_t<span class="token punctuation">,</span> <span class="token keyword">int</span><span class="token punctuation">(</span><span class="token operator">*</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token keyword">const</span> <span class="token keyword">void</span><span class="token operator">*</span><span class="token punctuation">,</span> <span class="token keyword">const</span> <span class="token keyword">void</span><span class="token operator">*</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 在其他平台上使用 heapsort</span>
<span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">endif</span></span>

<span class="token comment">//比较两个元素的大小</span>
<span class="token keyword">int</span> <span class="token function">complong</span><span class="token punctuation">(</span><span class="token keyword">const</span> <span class="token keyword">void</span><span class="token operator">*</span> arg1<span class="token punctuation">,</span> <span class="token keyword">const</span> <span class="token keyword">void</span><span class="token operator">*</span> arg2<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">long</span> l1 <span class="token operator">=</span> <span class="token operator">*</span><span class="token punctuation">(</span><span class="token keyword">long</span><span class="token operator">*</span><span class="token punctuation">)</span>arg1<span class="token punctuation">;</span> <span class="token comment">// 强制转换参数类型为 long</span>
    <span class="token keyword">long</span> l2 <span class="token operator">=</span> <span class="token operator">*</span><span class="token punctuation">(</span><span class="token keyword">long</span><span class="token operator">*</span><span class="token punctuation">)</span>arg2<span class="token punctuation">;</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>l1 <span class="token operator">==</span> l2<span class="token punctuation">)</span>
        <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span>
    <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>l1 <span class="token operator">&lt;</span> l2<span class="token punctuation">)</span>
        <span class="token keyword">return</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span>
    <span class="token keyword">else</span>
        <span class="token keyword">return</span> <span class="token number">1</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">//线程的执行函数，使用heapsort算法对一部分数组进行排序，并在完成后等待其他线程</span>
<span class="token keyword">void</span><span class="token operator">*</span> <span class="token function">thr_fn</span><span class="token punctuation">(</span><span class="token keyword">void</span><span class="token operator">*</span> arg<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">long</span> idx <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">long</span><span class="token punctuation">)</span>arg<span class="token punctuation">;</span>  <span class="token comment">// 强制转换参数类型为 long</span>
    <span class="token function">heapsort</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>nums<span class="token punctuation">[</span>idx<span class="token punctuation">]</span><span class="token punctuation">,</span> TNUM<span class="token punctuation">,</span> <span class="token keyword">sizeof</span><span class="token punctuation">(</span><span class="token keyword">long</span><span class="token punctuation">)</span><span class="token punctuation">,</span> complong<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 使用 heapsort 进行排序</span>
    <span class="token function">pthread_barrier_wait</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>b<span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token comment">// 等待其他线程到达屏障</span>
    <span class="token keyword">return</span> <span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token keyword">void</span><span class="token operator">*</span><span class="token punctuation">)</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">//合并各个线程排序后的子数组，生成最终的有序数组</span>
<span class="token keyword">void</span> <span class="token function">merge</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">long</span> idx<span class="token punctuation">[</span>NTHR<span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// 存储每个线程当前处理的索引</span>
    <span class="token keyword">long</span> i<span class="token punctuation">,</span> minidx<span class="token punctuation">,</span> sidx<span class="token punctuation">,</span> num<span class="token punctuation">;</span>
    <span class="token keyword">for</span> <span class="token punctuation">(</span>i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> NTHR<span class="token punctuation">;</span> <span class="token operator">++</span>i<span class="token punctuation">)</span>
        idx<span class="token punctuation">[</span>i<span class="token punctuation">]</span> <span class="token operator">=</span> i <span class="token operator">*</span> TNUM<span class="token punctuation">;</span>
    <span class="token keyword">for</span> <span class="token punctuation">(</span>sidx <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> sidx <span class="token operator">&lt;</span> NUMNUM<span class="token punctuation">;</span> sidx<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        num <span class="token operator">=</span> LONG_MAX<span class="token punctuation">;</span>
        <span class="token keyword">for</span> <span class="token punctuation">(</span>i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> NTHR<span class="token punctuation">;</span> <span class="token operator">++</span>i<span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>idx<span class="token punctuation">[</span>i<span class="token punctuation">]</span> <span class="token operator">&lt;</span> <span class="token punctuation">(</span>i <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token operator">*</span> TNUM<span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> <span class="token punctuation">(</span>nums<span class="token punctuation">[</span>idx<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">]</span> <span class="token operator">&lt;</span> num<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
                num <span class="token operator">=</span> nums<span class="token punctuation">[</span>idx<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
                minidx <span class="token operator">=</span> i<span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span>
        snums<span class="token punctuation">[</span>sidx<span class="token punctuation">]</span> <span class="token operator">=</span> nums<span class="token punctuation">[</span>idx<span class="token punctuation">[</span>minidx<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
        idx<span class="token punctuation">[</span>minidx<span class="token punctuation">]</span><span class="token operator">++</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">unsigned</span> <span class="token keyword">long</span> i<span class="token punctuation">;</span>
    <span class="token keyword">struct</span> <span class="token class-name">timeval</span> start<span class="token punctuation">,</span> end<span class="token punctuation">;</span>
    <span class="token keyword">long</span> <span class="token keyword">long</span> startusec<span class="token punctuation">,</span> endusec<span class="token punctuation">;</span><span class="token comment">//记录开始时间、结束时间</span>
    <span class="token keyword">double</span> elapsed<span class="token punctuation">;</span><span class="token comment">//计算经过的时间</span>
    <span class="token keyword">int</span> err<span class="token punctuation">;</span>
    pthread_t tid<span class="token punctuation">;</span>

    <span class="token function">srandom</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token comment">// 初始化随机数生成器</span>
    <span class="token keyword">for</span> <span class="token punctuation">(</span>i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> NUMNUM<span class="token punctuation">;</span> <span class="token operator">++</span>i<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        nums<span class="token punctuation">[</span>i<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">random</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token comment">// 生成随机数并存储在 nums 数组中</span>
    <span class="token punctuation">}</span>
    <span class="token function">gettimeofday</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>start<span class="token punctuation">,</span> <span class="token constant">NULL</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token comment">// 获取开始时间</span>
    <span class="token function">pthread_barrier_init</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>b<span class="token punctuation">,</span> <span class="token constant">NULL</span><span class="token punctuation">,</span> NTHR <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token comment">// 初始化 pthread 屏障</span>
    <span class="token keyword">for</span> <span class="token punctuation">(</span>i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> NTHR<span class="token punctuation">;</span> <span class="token operator">++</span>i<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        err <span class="token operator">=</span> <span class="token function">pthread_create</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>tid<span class="token punctuation">,</span> <span class="token constant">NULL</span><span class="token punctuation">,</span> thr_fn<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token keyword">void</span><span class="token operator">*</span><span class="token punctuation">)</span><span class="token punctuation">(</span>i <span class="token operator">*</span> TNUM<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 创建线程</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>err <span class="token operator">!=</span> <span class="token number">0</span><span class="token punctuation">)</span>
            <span class="token function">err_exit</span><span class="token punctuation">(</span>err<span class="token punctuation">,</span> <span class="token string">&quot;can't create thread&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 处理线程创建错误</span>
    <span class="token punctuation">}</span>
    <span class="token function">pthread_barrier_wait</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>b<span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token comment">// 主线程等待所有线程到达屏障</span>
    <span class="token function">merge</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token comment">// 合并排序后的子数组</span>
    <span class="token function">gettimeofday</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>end<span class="token punctuation">,</span> <span class="token constant">NULL</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token comment">// 获取结束时间</span>

    startusec <span class="token operator">=</span> start<span class="token punctuation">.</span>tv_sec <span class="token operator">*</span> <span class="token number">1000000</span> <span class="token operator">+</span> start<span class="token punctuation">.</span>tv_usec<span class="token punctuation">;</span>
    endusec <span class="token operator">=</span> end<span class="token punctuation">.</span>tv_sec <span class="token operator">*</span> <span class="token number">1000000</span> <span class="token operator">+</span> end<span class="token punctuation">.</span>tv_usec<span class="token punctuation">;</span>
    elapsed <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">double</span><span class="token punctuation">)</span><span class="token punctuation">(</span>endusec <span class="token operator">-</span> startusec<span class="token punctuation">)</span> <span class="token operator">/</span> <span class="token number">1000000.0</span><span class="token punctuation">;</span> <span class="token comment">// 计算经过的时间</span>
    <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">&quot;sort took %.4f seconds\n&quot;</span><span class="token punctuation">,</span> elapsed<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 打印排序花费的时间</span>
    <span class="token keyword">for</span> <span class="token punctuation">(</span>i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> NUMNUM<span class="token punctuation">;</span> <span class="token operator">++</span>i<span class="token punctuation">)</span>
        <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">&quot;%ld\n&quot;</span><span class="token punctuation">,</span> snums<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token comment">// 打印排序后的数值</span>
    <span class="token function">exit</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br><span class="line-number">33</span><br><span class="line-number">34</span><br><span class="line-number">35</span><br><span class="line-number">36</span><br><span class="line-number">37</span><br><span class="line-number">38</span><br><span class="line-number">39</span><br><span class="line-number">40</span><br><span class="line-number">41</span><br><span class="line-number">42</span><br><span class="line-number">43</span><br><span class="line-number">44</span><br><span class="line-number">45</span><br><span class="line-number">46</span><br><span class="line-number">47</span><br><span class="line-number">48</span><br><span class="line-number">49</span><br><span class="line-number">50</span><br><span class="line-number">51</span><br><span class="line-number">52</span><br><span class="line-number">53</span><br><span class="line-number">54</span><br><span class="line-number">55</span><br><span class="line-number">56</span><br><span class="line-number">57</span><br><span class="line-number">58</span><br><span class="line-number">59</span><br><span class="line-number">60</span><br><span class="line-number">61</span><br><span class="line-number">62</span><br><span class="line-number">63</span><br><span class="line-number">64</span><br><span class="line-number">65</span><br><span class="line-number">66</span><br><span class="line-number">67</span><br><span class="line-number">68</span><br><span class="line-number">69</span><br><span class="line-number">70</span><br><span class="line-number">71</span><br><span class="line-number">72</span><br><span class="line-number">73</span><br><span class="line-number">74</span><br><span class="line-number">75</span><br><span class="line-number">76</span><br><span class="line-number">77</span><br><span class="line-number">78</span><br><span class="line-number">79</span><br><span class="line-number">80</span><br><span class="line-number">81</span><br><span class="line-number">82</span><br><span class="line-number">83</span><br><span class="line-number">84</span><br><span class="line-number">85</span><br><span class="line-number">86</span><br><span class="line-number">87</span><br></div></div><ul><li>这个例子给出了多个线程只执行一个任务时，使用屏障的简单情况。</li> <li>在更加实际的情况下，工作线程在调用pthread_barrier_wait函数返回后会接着执行其他的活动。</li> <li>在这个实例中，使用8个线程分解了800万个数的排序工作。每个线程用堆排序算法对100万个数进行排序。然后主线程调用一个函数对这些结果进行合并。</li> <li>并不需要使用 pthread_barrier_wait 函数中的返回值PTHREAD_BARRIER_SERIAL_THREAD 来决定哪个线程执行结果合并操作，因为我们使用了主线程来完成这个任务。</li> <li>这也是把屏障计数值设为工作线程数加1的原因，主线程也作为其中的一个候选线程。</li></ul></div></div> <!----> <div class="page-edit"><!----> <!----> <div class="last-updated"><span class="prefix">上次更新:</span> <span class="time">2024/6/3 14:54:44</span></div></div> <div class="page-nav-wapper"><div class="page-nav-centre-wrap"><a href="/pages/6b2468/" class="page-nav-centre page-nav-centre-prev"><div class="tooltip">第4章 Cpp多线程系统编程精要</div></a> <a href="/pages/3f1d21/" class="page-nav-centre page-nav-centre-next"><div class="tooltip">函数模板</div></a></div> <div class="page-nav"><p class="inner"><span class="prev">
        ←
        <a href="/pages/6b2468/" class="prev">第4章 Cpp多线程系统编程精要</a></span> <span class="next"><a href="/pages/3f1d21/">函数模板</a>→
      </span></p></div></div></div> <!----></main></div> <div class="footer"><!----> 
  Theme by
  <a href="https://github.com/xugaoyi/vuepress-theme-vdoing" target="_blank" title="本站主题">Vdoing</a> 
    | Copyright © 2023-2025
    <span>霜晨月</span></div> <div class="buttons"><div title="返回顶部" class="button blur go-to-top iconfont icon-fanhuidingbu" style="display:none;"></div> <div title="去评论" class="button blur go-to-comment iconfont icon-pinglun" style="display:none;"></div> <div title="主题模式" class="button blur theme-mode-but iconfont icon-zhuti"><ul class="select-box" style="display:none;"><li class="iconfont icon-zidong">
          跟随系统
        </li><li class="iconfont icon-rijianmoshi">
          浅色模式
        </li><li class="iconfont icon-yejianmoshi">
          深色模式
        </li><li class="iconfont icon-yuedu">
          阅读模式
        </li></ul></div></div> <!----> <!----> <!----></div><div class="global-ui"><canvas id="vuepress-canvas-cursor"></canvas></div></div>
    <script src="/assets/js/app.67adcfd9.js" defer></script><script src="/assets/js/4.9aaa1650.js" defer></script><script src="/assets/js/1.5474518c.js" defer></script><script src="/assets/js/3.593d14fc.js" defer></script><script src="/assets/js/60.5cd0051a.js" defer></script>
  </body>
</html>
